UIBarButtons disappeared in iOS 9.2 - ios

I have just upgraded one of my Apps under development to iOS 9.2 and I have found that all navigationbar UIBarButtons have just disappeared and are not shown on the navigation bar.
BTW: I'm using the UIBarButton custom class called BBBadgeBarButtonItem
from here
Update 1
Here is a snippet from the code I used to add the UIBarButtonItem
// Add the search button
UIImage* searchBtnImg = [UIImage imageNamed:#"searchBarButton"];
searchBtnImg = [self ipMaskedImage:searchBtnImg color:[UIColor pddAppSelectedIconColor]];
CGRect frame = CGRectMake(0, 0,searchBtnImg.size.width,searchBtnImg.size.height);
UIButton* searchBtn = [[UIButton alloc] initWithFrame:frame];
[searchBtn setBackgroundImage:searchBtnImg forState:UIControlStateNormal];
[searchBtn addTarget:self action:#selector(_searchContent) forControlEvents:UIControlEventTouchDown];
self.searchBarButtonItem = [[BBBadgeBarButtonItem alloc] initWithCustomUIButton:searchBtn];
self.searchBarButtonItem.shouldHideBadgeAtZero = YES;
self.searchBarButtonItem.badgeValue = #"0";
self.navigationItem.leftBarButtonItem = self.searchBarButtonItem;
Update 2
I see the following error message
<Error>: CGContextSetFillColorWithColor: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable.
and from this answer it seems to be an Apple Bug

I have figure it out, the ImageCatalog for random reason have make the custom BarButtonItem Unassigned for some unknown reason.... What a stupid thing ;(
Also, as I general note I have noticed that opening Xcode project for iOS9.1 on newer Xcode 7.2 or vice versa I got some of the already added images into the ImageCatalog to be in Unusigned state and the App couldn't see it until I fix that in ImageCatalog.

Related

How to set UIBackgroundConfiguration for UIButton?

I am struggling to change background properties of UIButton.
They can be easily modified using "Background Configuration" settings in Interface Builder:
but I don't understand how to set them programmatically.
For example, I try to do it like this:
UIBackgroundConfiguration * config = [UIBackgroundConfiguration listPlainCellConfiguration];
[config setStrokeColor: UIColor.labelColor];
but where should I assign this 'config' value? There is nothing like 'button.backgroundConfiguration' property or anything similar for UIButton object.
Note:
'configuration' property of UIButton object is not available in code (Objective C, Mac Catalyst, iOS 15) despite documentation statement, screenshot attached. Will be great to know the reason why this is so.
The background configuration is part of the UIButtonConfiguration (the button's configuration).
https://developer.apple.com/documentation/uikit/uibuttonconfiguration/3750780-background?language=objc
Here's an example of configuring a button created in the storyboard:
UIButtonConfiguration * con = self.button.configuration;
[con setTitle: #"Hello"];
UIBackgroundConfiguration * back = con.background;
[back setStrokeColor: UIColor.labelColor];
[back setStrokeWidth: 3];
[back setBackgroundColor: [UIColor redColor]];
self.button.configuration = con;
Result:
It happens that 'button.configuration.background' property is not available in the Xcode 13 beta 5 for Mac Catalyst.
One have to wait for newer Xcode release to get this feature working from code.

iOS 9 View Not appearing

I have noticed a that a uibutton is not showing in my app when run in iOS 9 (works fine in earlier OS versions). I know several have noticed a similar issue with storyboards (https://forums.developer.apple.com/thread/14003), though I am not using those and am instead building my view programmatically. There is no autolayout or autoresizingmask to worry about.
UIButton * button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = CGRectMake(SUG_MARGIN, self.mainView.bounds.size.height - 40 - margin, self.mainView.bounds.size.width-margin*2, 40);
[button setTitle:NSLocalizedString(#"...", nil) forState:UIControlStateNormal];
button.titleLabel.font = //...;
button.backgroundColor = //...;
[self.mainView addSubview:button];
NSLog(#"main:%#\nadd:%#\nsuper:%#", NSStringFromCGRect(self.mainView.frame), NSStringFromCGRect(button.frame), button.superview);
That nslog shows everything set up correctly.
I have similar buttons all over the app, but this is the only one that is not appearing, which is why I am scratching my head.
Refactored code to assign only measure frames in viewWillLayoutSubviews and it now works. strange.

TabBar Images not showing in iOS 7.1

I'm developing an app that has iOS 7.1 as its Deployment Target. So far everything works just fine, however there is one huge issue, the tab bar images are not showing up. This is confusing, because they show up when running on the iOS 8 Simulator, I'm using the Xcode 6 beta 1.
I've set up two images. The dimensions are 60x60 and 70x60. So size does not seem to be an issue. I've set the images up in Interface Builder. When logging the tab bar image to the console however, it returns nil. I've then tried setting the images up in code, and using the same method to log it to the console, I now have a memory address for the image, but it still does not show up.
When searching for an answer on Google and stackoverflow, I've found this method:
self.tabBarItem.image = [[UIImage imageNamed:#"IMAGE"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
self.tabBarItem.selectedImage = [[UIImage imageNamed:#"IMAGE"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
NSLog(#"%#", self.tabBarItem.image);
This worked for some users apparently, but not for me. I can not figure this out, I'd be glad if someone could help me.
I have a feeling about what you're doing, but it's still not clear.
You have a tab bar, but you have an outlet to the tab bar item itself.
That's not the way you would do it. You create an outlet to the tab bar, then add custom items with:
UIImage *defaultImage = [UIImage imageNamed:#"ImageName"];
UIImage *selectedImage = [UIImage imageNamed:#"ImageName"];
UITabBarItem *itemZero = [[UITabBarItem alloc] initWithTitle:#"Item One" image:defaultImage selectedImage:selectedImage];
UITabBarItem *itemOne = [[UITabBarItem alloc] initWithTitle:#"Item Two" image:defaultImage selectedImage:selectedImage];
NSArray *items = #[itemZero,itemOne];
[self.tabBar setItems:items animated:animated];

Why would this code work on iPhone 5, 4 and 4s but not 5s?

I have the below code for adding some UIBarButtons to my project. This works just fine on the following:
All on iOS 7.1
Simulator iPhone Retina 3.5 inch;
Simulator iPhone Retina 4 inch;
Simulator iPhone Retina 4 inch (64 bit)
Iphone 4
iPhone 4s
I don't have an iPhone 5 device to test though.
It doesn't work on a new iPhone 5s. Whats different?
Here is the code:
-(void)setupNavigationBar
{
self.saveSearchButton = [UIButton buttonWithType:UIButtonTypeCustom];
[self.saveSearchButton setImage:[UIImage imageNamed:#"Save Search"] forState:UIControlStateNormal];
[self.saveSearchButton setImage:[UIImage imageNamed:#"Save Search Active"] forState:UIControlStateHighlighted|UIControlStateSelected];
[self.saveSearchButton addTarget:self action:#selector(saveSearchButtonpressed)forControlEvents:UIControlEventTouchUpInside];
[self.saveSearchButton setFrame:CGRectMake(0, 0, 23, 31)];
self.changeLayutButton = [UIButton buttonWithType:UIButtonTypeCustom];
[self.changeLayutButton setImage:[UIImage imageNamed:#"View List"] forState:UIControlStateNormal];
[self.changeLayutButton setImage:[UIImage imageNamed:#"View Grid"] forState:UIControlStateHighlighted|UIControlStateSelected];
[self.changeLayutButton addTarget:self action:#selector(changeViewLayoutButtonPressed)forControlEvents:UIControlEventTouchUpInside];
[self.changeLayutButton setFrame:CGRectMake(0, 0, 23, 31)];
self.sortByButton = [UIButton buttonWithType:UIButtonTypeCustom];
[self.sortByButton setImage:[UIImage imageNamed:#"Sort By"] forState:UIControlStateNormal];
[self.sortByButton setImage:[UIImage imageNamed:#"Sort By Active"] forState:UIControlStateHighlighted|UIControlStateSelected];
[self.sortByButton addTarget:self action:#selector(sortByButtonPressed)forControlEvents:UIControlEventTouchUpInside];
[self.sortByButton setFrame:CGRectMake(0, 0, 23, 31)];
UIBarButtonItem *fixedItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
fixedItem.width = 20;
UIBarButtonItem *saveSearchButton = [[UIBarButtonItem alloc] initWithCustomView:self.saveSearchButton];
UIBarButtonItem *changeViewButton = [[UIBarButtonItem alloc] initWithCustomView:self.changeLayutButton];
UIBarButtonItem *sortByButton = [[UIBarButtonItem alloc] initWithCustomView:self.sortByButton];
NSArray *barbuttonitems = #[sortByButton, fixedItem, changeViewButton, fixedItem,saveSearchButton];
self.navigationItem.rightBarButtonItems = barbuttonitems;
self.lastLayoutUsed = [[NSUserDefaults standardUserDefaults]objectForKey:#"LastLayoutUsed"];
if ([self.lastLayoutUsed isEqualToString:#"GridLayout"]){
self.changeLayutButton.selected = YES;
[NSTimer scheduledTimerWithTimeInterval:0.0 target:self selector:#selector(highlightButton:) userInfo:self.changeLayutButton repeats:NO];
}
}
I've stepped through the code and all the properties are not nil and have valid values. I've checked all images are the correct side, too.
How would this work on every device on simulator and not an actual iPhone 5S?
Also, since the two phones (iPhone 4S and 5S) have the exact same screen width and iOS version I am really puzzled?
The buttons don't show up at all. No compiler warning and no console errors.
UPDATE
Tested the above code on a iPhone 5 and it works just fine. This leads be to believe it must be something to do with the 64bit of the iPhone 5S?
UPDATE 2
Removed all the code from the method and changed it to a very simple button like this:
self.saveSearchButton = [UIButton buttonWithType:UIButtonTypeCustom];
[self.saveSearchButton setTitle:#"Save" forState:UIControlStateNormal];
[self.saveSearchButton setTintColor:[UIColor blueColor]];
UIBarButtonItem *saveSearchButton = [[UIBarButtonItem alloc] initWithCustomView:self.saveSearchButton];
self.navigationItem.rightBarButtonItem = saveSearchButton;
This now doesn't work on any devices or simulators.
What am I doing wrong here?
Update 3 - Solution!
Right, so it was one simple thing that caused all this fuss. I declared my UIButtons as weak and not strong - I was under the impression that UI elements needed to be weak as they go off view so often?
I came to this answer with the help from the comments section.
This also does not explain why it works on iPhone 4S and iPhone 5 when declared as weak. It also worked on the 64 bit simulator declared as weak
Does this mean the 64bit simulator is to be used as a guide and actual testing must be done on the device as it seems the simulator is not accurate when it comes to UIKit testing?
I'd love to know more about this.
The problem was that [UIButton buttonWithType:UIButtonTypeCustom] doesn't leave an instance of the object as is stated in the Apple Docs. And the UIBarButton expects an instance of some sorts. https://developer.apple.com/library/ios/documentation/uikit/reference/UIButton_Class/UIButton/UIButton.html
The solution as is stated in the AppleDocs is using the alloc+init construction to make a button.
buttonWithType:
"This method is a convenience constructor for creating button objects
with specific configurations. If you subclass UIButton, this method
does not return an instance of your subclass. If you want to create an
instance of a specific subclass, you must alloc/init the button
directly."
#TotumusMaximus You have misunderstood what the documentation is saying. Note the first part:
If you subclass UIButton...
What this means is that if you try to do [MyAwesomeButton buttonWithType:UIButtonTypeCustom], the object that will be created will be a UIButton instance, NOT a MyAwesomeButton instance. In order to get a MyAwesomeButton instance, then you would need to do [[MyAwesomeButton alloc] init]. There are no button subclasses involved in the OP's problem, so this portion of the documentation does not apply here.
If your properties are weak, and you directly assign a newly created object to them, then by the rules of ARC they should immediately be deallocated and set to nil, since nothing else holds a strong reference to them. The fact that is was working on other devices / simulators was an accident of implementation.
There is no real need to use weak properties for UI elements now that views are not unloaded under memory pressure conditions any more. If you do still want to use weak properties, the correct pattern to use is to create a local (strong) reference, add it to a superview, and then assign to the property - at this point the button will have an owning reference (the superview) so will not be removed by ARC:
UIButton *button = [UIButton buttonWithType:whatever];
[self.view addSubview:button];
self.button = button;

UIButton doesn't listen to content mode setting?

firstButton is a UIButton of type Custom. I'm programmatically putting three of them across each cell of a table, thusly:
[firstButton setImage:markImage forState:UIControlStateNormal];
[firstButton setContentMode:UIViewContentModeScaleAspectFit];
[cell.contentView addSubview:firstButton];
Elsewhere, I'm telling it to clipToBounds. What I get is a crop of the center square of the image, rather than an aspect-scaled rendering of it. I've tried this lots of ways, including setting the mode property on firstButton.imageView, which also doesn't seem to work.
I had the same problem. I see this question is a little old, but I want to provide a clear and correct answer to save other folks (like me) some time when it pops up in their search results.
It took me a bit of searching and experimenting, but I found the solution. Simply set the ContentMode of the "hidden" ImageView that is inside the UIButton.
[[firstButton imageView] setContentMode: UIViewContentModeScaleAspectFit];
[firstButton setImage:[UIImage imageNamed:imageName] forState:UIControlStateNormal];
Perhaps that's what Dan Ray was alluding to in his accepted answer, but I suspect not.
If you're dealing with the UIButton's image (as opposed to it's backgroundImage), setting the contentMode on the UIButton itself or on its imageView has no effect (despite what other answers say).
Alternatively do this instead:
self.button.contentHorizontalAlignment = UIControlContentHorizontalAlignmentFill;
self.button.contentVerticalAlignment = UIControlContentVerticalAlignmentFill;
Or size your image accordingly.
OR just use a UIImageView (which properly respects contentMode) with a UITapGestureRecognizer attached to it, or a transparent UIButton on top of it.
Rather than setting the contentMode on the button itself, you'll want to set contentHorizontalAlignment and contentVerticalAlignment properties and (crucially) the contentMode for the button's imageView for any kind of aspect fill or fit:
button.contentHorizontalAlignment = .fill
button.contentVerticalAlignment = .fill
button.imageView?.contentMode = .scaleAspectFit
You can also do other things like aligning the button's image to the top. If you don't need an aspect fill or fit, you just can set the alignment by itself:
button.contentVerticalAlignment = .top
After a couple of hours of confusion, here's how I got it to work under iOS 3.2. As dusker mentioned, using setBackgroundImage instead of setImage did the job for me.
CGRect myButtonFrame = CGRectMake(0, 0, 250, 250);
UIImage *myButtonImage = [UIImage imageNamed:#"buttonImage"];
UIButton *myButton = [UIButton buttonWithType:UIButtonTypeCustom];
[myButton setBackgroundImage:myButtonImage forState:UIControlStateNormal];
[myButton setFrame: myButtonFrame];
[myButton setContentMode: UIViewContentModeScaleAspectFit];
The answer is to use a UIImageView with all the lovely Content Mode settings you want, and then layer a custom button on top of it. Dumb that you can't do that all in one shot, but it appears that you can't.
These two things (which are quite hard to find initially) will stretch your UIButton image to fit the button size:
one should always try to set such in the Storyboard rather than code.
Found a fix for this. Set the adjustsImageWhenHighlighted property of UIButton to NO.
UIButton *b = [[UIButton alloc] initWithFrame:rect];
[b setImage:image forState:UIControlStateNormal];
[b.imageView setContentMode:UIViewContentModeScaleAspectFill];
[b setAdjustsImageWhenHighlighted:NO];
Hope this helps. Feel free to comment below, I will follow up on any questions that you have.
My answer is similar to Kuba's. I needed my image to be programatically set.
UIImage *image = [[UIImage alloc] initWithContentsOfFile:...];
[button setBackgroundImage:image forState:UIControlStateNormal];
button.imageView.contentMode = UIViewContentModeScaleAspectFill; //this is needed for some reason, won't work without it.
for(UIView *view in button.subviews) {
view.contentMode = UIViewContentModeScaleAspectFill;
}
Only solution which worked for me:
[button setImage:image forState:UIControlStateNormal];
button.imageView.contentMode = UIViewContentModeScaleAspectFill;
button.contentHorizontalAlignment = UIControlContentHorizontalAlignmentFill;
button.contentVerticalAlignment = UIControlContentVerticalAlignmentFill;
Swift 3
self.firstButton.imageView?.contentMode = .scaleAspectFill
For anyone experiencing this on iOS 15 and Xcode 13, see Matt's answer in this other question.
The behavior of Xcode changed and now defaults UIButtons from the library to the plain style, which prevents the child image from scaling as expected.
Instead of setImage try setBackgroundImage
I believe we have a simple interface builder issue here - apparently the IB ignores any content-mode changes AFTER you have set the image-property.
the solution is as simple: set the content mode, remove previously set image-names (make sure you remove it in all states, default, highlighted etc.), then re-enter the desired image-names in all desired states - et voilĂ .
I also advice to have a look at the adjustsImageWhenHighlighted UIButton property to avoid weird deformations of the image, when the button is pressed.
In trying to figure this out, my method got a bit hackier as time went on, and I wound up subclassing UIButton and overriding setHighlighted:
For me it works to just knock down the image alpha to .5, because they're on a black background.
However, it only works if I comment out [super setHighlighted:] (where it appears the image-stretchy code is going on), which just doesn't feel like the right way to solve this at all...everything seems to be working fine, though. We'll see how it holds up as I keep working on it.
- (void)setHighlighted:(BOOL)highlight {
if (highlight) {
[self.imageView setAlpha:.5];
} else {
[self.imageView setAlpha:1];
}
// [super setHighlighted:highlight];
}
If anyone looking for answer that work in iOS 6 and iOS 7 and storyboard:
You can set image in your storyboard:
And then:
for(UIView* testId in self.subviews) {
if([testId isKindOfClass:[UIImageView class]])
[testId setContentMode:UIViewContentModeScaleAspectFill];
}
If the UIButton does not seem to listen to the layout constraint settings, do check whether the images are larger than the button size. Always use the #2x and #3x images for retina resolutions.

Resources