How to properly refresh a UINavigationBar? - ios

In relation to this question: How to change Back button text from within the child view controller? I am searching for a propery way to refresh the navigation bar after changing the back button title with previousViewController.navigationItem.backBarButtonItem?.title = "New Title".
The (not so ideal?) solution from the linked question:
if let navigationController = self.navigationController {
navigationController.popViewControllerAnimated(false)
navigationController.pushViewController(self, animated: false)
}
Edit:
Apparently changing the layer frame forces the navigation bar to refresh. Not a solution, but a less expensive(?) workaround I guess:
if let navigationController = self.navigationController {
navigationController.navigationBar.layer.frame.insetInPlace(dx: 0.1, dy: 0)
navigationController.navigationBar.layer.frame.insetInPlace(dx: -0.1, dy: 0)
}

After trying various methods to refresh, I find this is the least ugly solution that seems to work (back then on iOS 10 but apparently not currently on iOS 13, i.e., don't count on this):
guard let navigation = navigationController,
!(navigation.topViewController === self) else {
return
}
let bar = navigation.navigationBar
bar.setNeedsLayout()
bar.layoutIfNeeded()
bar.setNeedsDisplay()
Other methods tried:
Presenting a view controller (causes screen to flicker in some cases)
Hiding and re-showing the bar (breaks bar if half-way between backswipe to previous VC)
Setting the bar's layer's frame (does not seem to work reliably, and is explicitly forbidden by the documentation for navigationBar)

This works for me
_ = navigationController.view.snapshotView(afterScreenUpdates: true)

UIButton *leftbtn = [UIButton buttonWithType:UIButtonTypeCustom] ;
[leftbtn addTarget:self action:#selector(city:) forControlEvents:UIControlEventTouchUpInside];
[leftbtn setImage:[UIImage imageNamed:#"location"] forState:UIControlStateNormal];
leftbtn.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
[leftbtn sizeToFit];
self.citybtn = leftbtn;
UIBarButtonItem* cityBtn = [[UIBarButtonItem alloc] initWithCustomView:leftbtn];
UIBarButtonItem *left_fixedSpaceBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
left_fixedSpaceBarButtonItem.width = -17;
self.navigationItem.leftBarButtonItems = #[left_fixedSpaceBarButtonItem,cityBtn];
.........
when u change
[self.citybtn setTitle:city forState:UIControlStateNormal];
[self.citybtn sizeToFit];

One solution would be to have a function, which changes the UIBarButtonItem completely by removing/hiding the back button and showing a custom UIBarButtonItem in its place with the navigationItem.leftBarButtonItem property. Surely not ideal but the button is not meant to be changed mid-VC lifecycle so I guess you could try. In that sense there is no "proper" way as this is not considered standard behaviour.
It worked for me when I added this function to a button on a sample View Controller:
func changeBackButton() {
navigationItem.hidesBackButton = true
navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Test", style: .plain, target: self, action: #selector(test))
}

Related

Change weight and color of System Symbol UIImage in Swift [duplicate]

How come the icon info.png stays blue and don't comes with the original color of that image? I am using the following code below:
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:#"info.png"]
style:UIBarButtonItemStylePlain
target:self
action:#selector(info:)];
By default, image in UINavigationBar's bar button items is rendered using template mode. You can set it to original.
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithImage:[[UIImage imageNamed:#"info.png"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal]
style:UIBarButtonItemStylePlain
target:self
action:#selector(info:)];
Swift 3:
let image : UIImage? = UIImage.init(named: "heart.png")!.withRenderingMode(.alwaysOriginal)
I know this is too late to answer this question but I see there is a very simple way to solve this issue instead of doing some changes in the code
using Xcode
Go to the Assets --Select Image --- check Render as and select Original image instead of default property .
You can it from assets as well. Go to Assets.xcassets >> Select the image that is being used as barbutton item image. Tap on attribute inspector in right side panel. Choose render as to orignial image. It will be default earlier. You will now see colored image.
Swift 4:
let image = UIImage(named: "imageName")?.withRenderingMode(.alwaysOriginal)
navigationItem.leftBarButtonItem = UIBarButtonItem(image: image, style: .plain, target: self, action: #selector(leftBarButtonPressed))
For Swift 2.1+ it would look like this:
let image : UIImage? = UIImage(named:"myImage.png")!.imageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal)
or simply
let image : UIImage? = UIImage(named:"myImage.png")!.imageWithRenderingMode(.AlwaysOriginal)
Because the color of barButtonItems in your app is related to the tintColor property on the application's window.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window.tintColor = [UIColor redColor];
return YES;
}
Ok, got it... I set the image to it's original state first.
UIImage *image = [[UIImage imageNamed:#"info.png"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithImage:image
style:UIBarButtonItemStylePlain
target:self
action:#selector(info:)];
Change the bar button item tint color from the storyboard. Or color from the image in storyboard.
The color should be your expected color as hex or rgb.

Change UISearchBar Cancel button to icon

I want to change the UISearchBar's Cancel button to one that only has an image, and no text. Here's where I got to so far from original behaviour
to this
A step in the right direction, but the problem is the button is too wide. When I debug the view, I can see that it has button label insets of 11 points on left and right. Does anyone know how to make the button fit the content size? The image is square.
Here's my code for customising the button:
UIBarButtonItem *barButtonAppearanceInSearchBar;
if (IOS9) {
barButtonAppearanceInSearchBar = [UIBarButtonItem appearanceWhenContainedInInstancesOfClasses:#[[UISearchBar class]]];
} else {
barButtonAppearanceInSearchBar = [UIBarButtonItem appearanceWhenContainedIn:[UISearchBar class], nil];
}
barButtonAppearanceInSearchBar.image = [UIImage imageNamed:#"Close"];
barButtonAppearanceInSearchBar.title = nil;
Another weird issue is that when I dismiss the search bar and activate it again, the button image turns dark (it's still there, I can see it when debugging the views), so it looks like this
Any idea how to keep the icon white? I tried this method below, but without results:
- (void)willPresentSearchController:(UISearchController *)searchController {
searchController.searchBar.tintColor = [UIColor whiteColor];
}
Use image rendering and tint color.
barButtonAppearanceInSearchBar.image = [[UIImage imageNamed:#"Close"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
barButtonAppearanceInSearchBar.tintColor = [UIColor whiteColor];
barButtonAppearanceInSearchBar.title = nil;
Here's the Swift 4.1 Version of #jamesBlake 's answer:
func setUpSearchBar() {
let barButtonAppearanceInSearchBar: UIBarButtonItem?
barButtonAppearanceInSearchBar = UIBarButtonItem.appearance(whenContainedInInstancesOf: [UISearchBar.self])
barButtonAppearanceInSearchBar?.image = UIImage(named: "Home.png")?.withRenderingMode(.alwaysTemplate)
barButtonAppearanceInSearchBar?.tintColor = UIColor.white
barButtonAppearanceInSearchBar?.title = nil
}

Custom Back Button With Image

What I'd like to do is alter the height of the back button. However, as I understand it, the only option to alter is width. So, I thought I'd create a custom back button with my own, smaller, image. Now I've done this using the viewDidLoad method with the code below:
//Setup navigation bar
navigationController?.navigationItem.backBarButtonItem = UIBarButtonItem(image:UIImage(named:"back_arrow.png"), style:UIBarButtonItemStyle.Plain, target:nil, action:nil)
navigationController?.navigationItem.backBarButtonItem!.title = ""
However, the back button remains blue, large, and has the title 'Back'. How can I get this code to work properly? The debugger says it is running, but it is not changing anything.
I'm going to show you how to do this in the entire app, not just one page.
To change the default image of the back button, put the following in your app delegate didFinishLaunchingWithOptions::
Swift:
let backArrowImage = UIImage(named: "customImage")
let renderedImage = backArrowImage?.imageWithRenderingMode(.AlwaysOriginal)
UINavigationBar.appearance().backIndicatorImage = renderedImage
UINavigationBar.appearance().backIndicatorTransitionMaskImage = renderedImage
Obj-c:"
UIImage *backArrowImage = [UIImage imageNamed:#"customImage"];
UIImage *renderedImage = [backArrowImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
[UINavigationBar appearance].backIndicatorImage = renderedImage;
[UINavigationBar appearance].backIndicatorTransitionMaskImage = renderedImage;
To remove the "Back" text from the button, add this category to your AppDelegate.m file (or your own category):
Not sure how to do this in Swift yet, so here's the Obj-c version:
#implementation UINavigationItem (LuxeCustomization)
/**
Removes text from all default back buttons so only the arrow or custom image shows up
*/
-(UIBarButtonItem *)backBarButtonItem
{
return [[UIBarButtonItem alloc] initWithTitle:#"" style:UIBarButtonItemStylePlain target:nil action:nil];
}
#end
For color you have to set the tint color on navBar, also you can set navigationItem.backBarButtonItem to nil and use leftbarButtonItem with custom button image.

How to get an image for a "Star" (Favorite) UIBarButtonItem?

I am new to iOS. I'm looking to put a "Star" button on the top menu of my app. Is there a way to get the star button built into iOS similar to UIBarButtonSystemItemAdd
If not, how can I get an image similar to the common favorite(s) star used by Apple in iOS?
Similar to this:
A simple and quick way is to use FontawesomeKit.
You can easily create the button like this:
FAKFontAwesome *icon = [FAKFontAwesome starIconWithSize:20];
[icon addAttribute:NSForegroundColorAttributeName value:[UIColor whiteColor]];
UIBarButtonItem *favButton = [[UIBarButtonItem alloc] initWithImage:[icon imageWithSize:CGSizeMake(20, 20)]
style:UIBarButtonItemStylePlain
target:self
action:#selector(doSomething:)];
I think it's not possible. But if you want some icons, you can check this link, another one.
EDIT:
You can also extract the Artwork of a component: See this repository
Following up what andreamazz said, here's code to have a colored star (favorite) button in the navBaritem (swift):
let favAwesome = FAKFontAwesome.starIconWithSize(30.0) as FAKFontAwesome
favAwesome.addAttribute(NSForegroundColorAttributeName, value: UIColor.whiteColor())
let favBtn = UIButton.buttonWithType(UIButtonType.System) as UIButton
favBtn.frame = CGRectMake(0,0,30,30)
favBtn.setImage(favAwesome.imageWithSize(CGSizeMake(30, 30)), forState: .Normal)
favBtn.addTarget(self, action: "favorite", forControlEvents: UIControlEvents.TouchUpInside)
// favBarBtn is a class instance so we can access in the action method
favBarBtn = UIBarButtonItem(customView: favBtn)
navigationItem.rightBarButtonItem = favBarBtn
// your custom code here to find isFavorite
if isFavorite {
favBtn.tintColor = UIColor.redColor()
} else {
favBtn.tintColor = UIColor.blueColor()
}
then there is the "favorite" method to change the color (and set/unset favorite):
func favorite() {
// your custom favoriting code here
let favBtn = favBarBtn?.customView as UIButton!
if isFavorite {
favBtn.tintColor = UIColor.redColor()
} else {
favBtn.tintColor = UIColor.blueColor()
}
}

weird background behind navigation bar button item created from image

Just trying to add a button to a navigation bar from an image.
code:
UIBarButtonItem *newConvoButton = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:#"convos_new.png"] style:UIBarButtonItemStyleBordered target:self action:#selector(newConvoInit:)];
self.navigationItem.rightBarButtonItem = newConvoButton;
result:
(It should be just the dark image without the blue button in the background.)
This is likely overkill for what you want. But I have a good feeling that this will make your life a whole lot easier. The following will give you just an image without any UIBarButtonItem attributes.
UIImage *menuImage = [UIImage imageNamed:#"navBarMenuButton.png"];
UIButton *leftButton = [UIButton buttonWithType:UIButtonTypeCustom];
leftButton.frame = CGRectMake(0, 0, menuImage.size.width, menuImage.size.height);
[leftButton setBackgroundImage:menuImage forState:UIControlStateNormal];
aController.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:leftButton];
This method implements a custom UIButton, which given the frame of the UIImage you're using, will give you nothing else but the image of your choice added to your UINavigationBar.
A bonus is that you don't have to worry about re-sizing anything in case the image ever changes in the future because the frame inherits from the UIImage.
Best of luck!

Resources