Display issue with customized UINavigationBar - objective-c

I'm currently encountering an issue with the customized navigation bar of my app, especially on iPad Pro and iPad.
I've extended UINavigationBar to create a personalized top bar.
I'm only using Objectiv-C, and here is the initialization function :
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:(NSCoder *)aDecoder];
if (self)
{
[self setTranslucent:FALSE];
UIImageView *mylogo = [[UIImageView alloc] initWithImage:[self imageWithImage:[UIImage imageNamed:#"minilogo.png"] scaledToSize:CGSizeMake(200, 25)]];
[self.topItem setTitleView:mylogo];
menuButton = [UIButton buttonWithType:UIButtonTypeCustom];
menuButton.frame = CGRectMake(0, 0, 44, 44);
[menuButton setImage:[UIImage imageNamed:#"menuButton.png"] forState:UIControlStateNormal];
[menuButton setContentMode:UIViewContentModeCenter];
CALayer *menuBorder = [CALayer layer];
[menuBorder setFrame:CGRectMake(43, 0, 1, 44)];
[menuBorder setBackgroundColor:[[UIColor blackColor] CGColor]];
[menuButton.layer addSublayer:menuBorder];
[self.topItem setLeftBarButtonItem: [[UIBarButtonItem alloc] initWithCustomView:menuButton]];
searchButton = [UIButton buttonWithType:UIButtonTypeCustom];
searchButton.frame = CGRectMake(0, 0, 44, 44);
UIImage *searchImage = [self imageWithImage:[UIImage imageNamed:#"boutonRecherche.png"] scaledToSize:CGSizeMake(44, 44)];
[searchButton setBackgroundImage:searchImage forState:UIControlStateNormal];
[searchButton setContentMode:UIViewContentModeCenter];
CALayer *searchBorder = [CALayer layer];
[searchBorder setFrame:CGRectMake(0, 0, 1, 44)];
[searchBorder setBackgroundColor:[[UIColor blackColor] CGColor]];
[searchButton.layer addSublayer:searchBorder];
[self.topItem setRightBarButtonItem:[[UIBarButtonItem alloc] initWithCustomView:searchButton]];
if (#available(iOS 15.0, *))
{
UINavigationBarAppearance *navBarApparance = [[UINavigationBarAppearance alloc] init];
[navBarApparance setBackgroundImage:[UIImage imageNamed:#"fd_entete.png"]];
[self setStandardAppearance:navBarApparance];
[self setCompactAppearance:navBarApparance];
[self setScrollEdgeAppearance:navBarApparance];
}
else
{
[self setOpaque:YES];
[self setBackgroundImage:[UIImage imageNamed:#"fd_entete.png"] forBarMetrics:UIBarMetricsDefault];
}
}
return self;
}
My navigation bar is added to the view using the storyboard and I specifying its class name in the identity inspector tab.
I've set layout constraints so the content of my view is right under this navigation bar.
Here is a sample with a simple controller having the navigation bar and a webview beneath :
The issue I'm having only concerns iPads. Here is a screenshot of how it looks like on an iPad pro :
(the grey rect is just to illustrate that I've shorten the screenshot)
But surprisingly, the navigation bar render correctly if the view beneath is a UITableView, but it still have the border issue for the buttons - understand here that the button border is missing some pixels on top and bottom :
(it is also how the nav bar looks like on an iPad for any view controller)
In comparison, I don't have the issue on iPhones :
Any help is appreciated.

Related

ImagePickerController on iOS 11 not showing rightBarButton But it works on tapping

I am facing weird issue while running app using Xcode 9 on iOS 11.I've to show cancel button in navigation bar right to move back on parent controller.It was working fine till iOS 10.3 .
I've tried with setting tint color of navigation bar & .topItem.rightBarButtonItem . With handling navigation delegate methods ,cancel button becomes visible when we select any album & comes back album list view.
here is the code, i've used for showing cancel button
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
//Add cancel button since it was not visible even applying tint color to imagepickerviewcontroller
UIButton* customButton = [UIButton buttonWithType:UIButtonTypeCustom];
[customButton setFrame:CGRectMake(0, 0, 80, 44)];
[customButton setTitleColor:self.tintColor forState:UIControlStateNormal];
[customButton.titleLabel setTextAlignment:NSTextAlignmentRight];
[customButton setTitle:NSLocalizedString(#"CF_CANCEL", #"Cancel") forState:UIControlStateNormal];
[customButton addTarget:self action:#selector(cancelButtonAction) forControlEvents:UIControlEventTouchUpInside];
UINavigationBar *navigationBar = navigationController.navigationBar;
navigationBar.barStyle = UIBarStyleDefault;
navigationBar.tintColor = [UIColor redColor];
UINavigationItem *pickerNavBarTopItem = navigationBar.topItem;
UIBarButtonItem *cancelBarItem = [[UIBarButtonItem alloc]initWithCustomView:customButton];
pickerNavBarTopItem.rightBarButtonItem = cancelBarItem;
}
- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(nonnull UIViewController *)viewController animated:(BOOL)animated {
//Add cancel button since it was not visible even applying tint color to imagepickerviewcontroller
UIButton* customButton = [UIButton buttonWithType:UIButtonTypeCustom];
[customButton setFrame:CGRectMake(0, 0, 80, 44)];
[customButton setTitleColor:self.tintColor forState:UIControlStateNormal];
[customButton.titleLabel setTextAlignment:NSTextAlignmentRight];
[customButton setTitle:NSLocalizedString(#"CF_CANCEL", #"Cancel") forState:UIControlStateNormal];
[customButton addTarget:self action:#selector(cancelButtonAction) forControlEvents:UIControlEventTouchUpInside];
UINavigationBar *navigationBar = navigationController.navigationBar;
navigationBar.barStyle = UIBarStyleDefault;
navigationBar.tintColor = [UIColor redColor];
UINavigationItem *pickerNavBarTopItem = navigationBar.topItem;
UIBarButtonItem *cancelBarItem = [[UIBarButtonItem alloc]initWithCustomView:customButton];
pickerNavBarTopItem.rightBarButtonItem = cancelBarItem;
}
- (void)cancelButtonAction{
if (self.imageCompletionBlock != nil) {
self.imageCompletionBlock(nil, self.takePhotoOption);
}
[self.imagePickerController dismissViewControllerAnimated:YES completion:nil];
}
I've tried with overriding viewWillLayoutSubviews in UIImagePickerController extension
#implementation UIImagePickerController (FFGNavbar)
-(void)viewWillLayoutSubviews
{
[super viewWillLayoutSubviews];
[self.navigationBar setTintColor:[UIColor cf_themeColor]];
self.navigationBar.topItem.rightBarButtonItem.tintColor = [UIColor cf_themeColor];
[self.navigationBar.topItem.rightBarButtonItem setEnabled:true];
}
Please help me what else can i do to make it work like before.
I've taken screenshot when i tapped on top right then this cancel button becomes visible .
Thanks for valuable answers. I was able to resolve issue by removing UIBarButtonItem appearance code from UINavigationController subclass & used with setting custom button.

UINavigationBar - change UIBarButtonItem positions

I am using a UINavigationController and its bar within my app.
Now I want to change the leftBarButtonItem and rightBarButtonItem positions.
I want them to be on a different x and y position having a custom witdth and height.
But the problem is, that changing the frame does not change anything with the barButtonItem. I do not now why, but the frame always gets resetted to its original coordinates.
Here is my code which I call in viewWillAppear:
UINavigationItem *navItem = [[self.navigationBar items] lastObject];
UIView *rightview = [[UIView alloc] initWithFrame:CGRectMake(0,0,66,30)];
rightview.backgroundColor = [UIColor blueColor];
//add custom view
UIBarButtonItem *search = [[UIBarButtonItem alloc] initWithCustomView:rightview];
navItem.leftBarButtonItem = search;
The view is not starting at 0/0, the x position for example is at 5px insetead of 0.
Any suggestions on how to solve this issue?
This worked for me for the leftBarButtonItem:
If you want the UIBarButtonItem to show exactly at position (0, 0), you can create a UIButton, sets its frame to (-5, 0, width, height), assuming that the offet of a left barbutton item is (5, 0), and place that in a UIView that you set as leftBarButtonItem.
#import "UINavigationItem+CSAButtons.h"
#implementation UINavigationItem (CSAButtons)
- (void) makeLeftBarButtonWithTarget:(id)target action:(SEL)action {
UIImage * imageNormal = [UIImage imageNamed:#"normal.png"];
UIImage * imageHighlighted = [UIImage imageNamed:#"highlighted.png"];
UIImage * imageDisabled = [UIImage imageNamed:#"disabled.png"];
// create your button
UIButton * button = [UIButton buttonWithType:UIButtonTypeCustom];
button.exclusiveTouch = YES;
[button addTarget:target action:action forControlEvents:UIControlEventTouchUpInside];
[button setBackgroundImage:imageNormal forState:UIControlStateNormal];
[button setBackgroundImage:imageHighlighted forState:UIControlStateHighlighted];
[button setBackgroundImage:imageDisabled forState:UIControlStateDisabled];
// set the frame of the button (better to grab actual offset of leftbarbuttonitem instead of magic numbers)
button.frame = CGRectMake(-5.0, 0.0, imageNormal.size.width, imageNormal.size.height);
UIView * view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, imageNormal.size.width, imageNormal.size.height)];
[view addSubview:button];
// set the barbuttonitem to be the view
UIBarButtonItem * barButtonItem = [[UIBarButtonItem alloc] initWithCustomView:view];
self.leftBarButtonItem = barButtonItem;
}
I dont think you have the ability to move a UIBarButtonItem but you might be able to achieve the same effect by adding that UIView element as a subview to the navigationBar (or navItem). You'll need to play with it a bit. It should allow for much more flexibility.
hope this helps.

Overlay not scrolling with UITableView - iOS

I have a UITableView class that uses the following methods to call a loading overlay when going to the next screen. The problem is that this loading screen does not scroll with the list... So if you scroll a little bit and click on something, the loading screen doesn't show (because it's at the top). How can I get the loading screen to stay on top of the UITableView at all times? Please know that each UITableView is contained within a UINavBar, which is contained within a UITabBar. Here is my code:
-(void)createLoadScreen
{
CGRect screenRect = [self.view bounds];
CGRect overlayFrame = CGRectMake(0.0, 0.0, screenRect.size.width, screenRect.size.height);
overlay = [[UIView alloc] initWithFrame:overlayFrame];
overlay.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.7];
CGRect frame = CGRectMake(screenRect.size.width / 2 - 25.0, screenRect.size.height / 2 - 70, 25.0, 25.0);
loading = [[UIActivityIndicatorView alloc] initWithFrame:frame];
[loading setActivityIndicatorViewStyle:UIActivityIndicatorViewStyleWhiteLarge];
[loading sizeToFit];
loading.autoresizingMask = (UIViewAutoresizingFlexibleLeftMargin |
UIViewAutoresizingFlexibleRightMargin |
UIViewAutoresizingFlexibleTopMargin |
UIViewAutoresizingFlexibleBottomMargin);
loading.hidesWhenStopped = YES;
[overlay addSubview:loading];
}
-(void)showActivityIndicator
{
[self.view addSubview:overlay];
[loading startAnimating];
}
-(void)removeActivityIndicator {
[loading stopAnimating];
[overlay removeFromSuperview];
}
Are you using a UITableViewController?
You should be able to fix your issue by adding the overlay view to the table's superview instead of the table view itself. This way your overlay is actually above and separate from your scrolling table view
-(void)showActivityIndicator
{
[[self.view superview] addSubview:overlay];
[loading startAnimating];
}

vertically aligning UINavigationItems

I have customized the UINavigationBar's height to 100px and I'd like to add buttons onto this customized bar.
All is good except the button seems to want to sit on the bottom of the nav bar no matter what. I can't get it to align to the center or the top of the nav bar.
Here is the code; first I create a navigation controller in the app delegate and add one button on the right.
// set main navigation controller
temp *tempc = [[temp alloc] initWithNibName:#"temp" bundle:nil];
self.navigationController = [[UINavigationController alloc] initWithRootViewController:tempc];
UIBarButtonItem *navItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:nil action:nil];
[tempc.navigationItem setRightBarButtonItem:navItem];
[self.window addSubview:self.navigationController.view];
[self.window makeKeyAndVisible];
then I resize the navigation bar in the "temp" view controller like so:
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
CGRect frame = CGRectMake(0.0f, 0.0f, 320.0f, 100.0f);
[self.navigationController.navigationBar setFrame:frame];
[self.navigationController.navigationBar setAutoresizingMask:UIViewAutoresizingFlexibleBottomMargin];
}
I've also tried to add a custom view to the rightBarButtonItem but I can't get the added custom view to touch the top completely.
And the code for this unfortunate attempt:
// set main navigation controller
temp *tempc = [[temp alloc] initWithNibName:#"temp" bundle:nil];
self.navigationController = [[UINavigationController alloc] initWithRootViewController:tempc];
UIView *customView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 36.0f, 80.0f)];
[customView setBackgroundColor:[UIColor blueColor]];
UIButton *customButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[customButton setFrame:CGRectMake(0, 22.0f, 36.0f, 36.0f)];
[customButton setTitle:#"+" forState:UIControlStateNormal];
[customView addSubview:customButton];
UIBarButtonItem *customItem = [[UIBarButtonItem alloc] initWithCustomView:customView];
Does anyone know how to vertically align UIBarButtonItems on a UINavigationBar?
This will move page title and all buttons up 20 px:
[[UINavigationBar appearance] setTitleVerticalPositionAdjustment:-20.0 forBarMetrics:UIBarMetricsDefault];
[[UIBarButtonItem appearance] setBackgroundVerticalPositionAdjustment:-20.0 forBarMetrics:UIBarMetricsDefault];
The current accepted answer from alhcr (Option 1) does not work if you only have a rightBarButtonItem and no left. It is also making an assumption about the subview ordering of the UINavigationBar which is not guaranteed and could very well change in a future version of iOS. Here is a better way of customizing navigation button frames:
// UINavigationBar subclass
- (void)layoutSubviews {
[super layoutSubviews];
UINavigationItem *navigationItem = [self topItem];
for (UIView *subview in [self subviews]) {
if (subview == [[navigationItem rightBarButtonItem] customView]) {
CGRect newRightButtonRect = CGRectMake(...new rect...);
[subview setFrame:newRightButtonRect];
}
else if (subview == [[navigationItem backBarButtonItem] customView]) {
CGRect newBackButtonRect = CGRectMake(...new rect...);
[subview setFrame:newBackButtonRect];
}
}
}
Option 1:
create UINavigationBar subclass
inside this class override - (void)layoutSubviews where you will reposition UIBarButtonItems
in Interface Builder set UINavigationBar class to UINavigationBar subclass
Example:
inside subclass of UINavigationBar:
- (void)layoutSubviews {
int index = 0;
for ( UIButton *button in [self subviews] ) {
if(index == 1) {
[button setFrame:CGRectMake(5,40,60,40)]; //leftBarButtonItem
} else if(index == 2) {
[button setFrame:CGRectMake(275,40,40,40)]; //rightBarButtonItem
}
++index;
}
}
view controller:
- (void)viewDidLoad {
...
UIBarButtonItem *navItem1 = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:nil action:nil];
UIBarButtonItem *navItem2 = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:nil action:nil];
[self.navigationItem setRightBarButtonItem:navItem1];
[self.navigationItem setLeftBarButtonItem:navItem2];
CGRect frame = CGRectMake(0.0f, 0.0f, 320.0f, 100.0f);
[self.navigationController.navigationBar setFrame:frame];
...
}
Option 2 (inserting UIBarButtonItem doesn't work):
UINavigationBar *navigationBar = [[UINavigationBar alloc] initWithFrame:CGRectMake(0,0,320,100)];
[self.view addSubview:navigationBar];
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[button setFrame:CGRectMake(0,25,50,50)];
[navigationBar addSubview:button];
I found the solution of this problem by making adjustment in the Image Edge Insets of the custom button. I had the requirement in the app to increase the height of the navigation bar and after increasing the height makes the rightBarButtonItem and leftBarButtonItem images unpositioned problem.
Find the code below:-
UIImage *image = [[UIImage imageNamed:#"searchbar.png"];
UIButton* searchbutton = [UIButton buttonWithType:UIButtonTypeCustom];
[searchbutton addTarget:self action:#selector(searchBar:) forControlEvents:UIControlEventTouchUpInside];
searchbutton.frame = CGRectMake(0,0,22, 22);
[searchbutton setImage:image forState:UIControlStateNormal];
[searchbutton setImageEdgeInsets:UIEdgeInsetsMake(-50, 0,50, 0)];
// Make BarButton Item
UIBarButtonItem *navItem = [[UIBarButtonItem alloc] initWithCustomView:searchbutton];
self.navigationItem.rightBarButtonItem = navItem;

UINavigationItem leftBarButton is hidden behind it's background

I have a UINavigationBar with a custom background and my own back button. I did the following to try achieve this.
//custom background
UIImageView *background = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"navigation-bar.png"]] autorelease];
[self.navigationController.navigationBar insertSubview:background atIndex:0];
//custom back button
UIImage *buttonImage = [UIImage imageNamed:#"btn_back.png"];
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button setImage:buttonImage forState:UIControlStateNormal];
button.frame = CGRectMake(0, 0, buttonImage.size.width, buttonImage.size.height);
[button addTarget:self action:#selector(backPressed:) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *customBarItem = [[UIBarButtonItem alloc] initWithCustomView:button];
self.navigationItem.leftBarButtonItem = customBarItem;
[customBarItem release];
The back button always works, but it can only be seen if I don't apply my custom background. Could someone please point out how to achieve both?
Thanks in advance.
Ricky.
You can set background image of navigation bar by using this
#implementation UINavigationBar (UINavigationBarCategory)
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx
{
if([self isMemberOfClass:[UINavigationBar class]])
{
UIImage *image;
image=[UIImage imageNamed:#"nav_bar_img"];
CGContextClip(ctx);
CGContextTranslateCTM(ctx, 0, image.size.height);
CGContextScaleCTM(ctx, 1.0, -1.0);
CGContextDrawImage(ctx,
CGRectMake(0, 0, self.frame.size.width, self.frame.size.height), image.CGImage);
}
else
{
[super drawLayer:layer inContext:ctx];
}
}
#end
I ended up doing the following.
#implementation UINavigationBar (Category)
- (void)drawRect:(CGRect)rect
{
MyAppDelegate *delegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
[delegate.navImage drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
}
#end
Every time I want to change the background of the bar, I change the delegate's navImage property and call -setNeedsDisplay on my navigation bar.
Thanks for your help.