UIBarButtonItem with custom view not properly aligned on iOS 7 when used as left or right navigation bar items - uibutton

The following code works up through iOS 6:
UIButton *myButton = nil;
myButton = [UIButton buttonWithType:UIButtonTypeCustom];
myButton.bounds = CGRectMake(0,0,44,30);
// setup myButton's images, etc.
UIBarButtonItem *item = nil;
item = [[UIBarButtonItem alloc] initWithCustomView:customButton];
This is how the button is supposed to be aligned:
However, on iOS 7, the button appears to be offset from the right or left by too many pixels:
How can I get my custom bar button items to be aligned properly?

Works until iOS11!
You can use negative flexible spaces and rightBarButtonItems property instead of rightBarButtonItem:
UIBarButtonItem *spacer = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
spacer.width = -10; // for example shift right bar button to the right
self.navigationItem.rightBarButtonItems = #[spacer, yourBarButton];

In order to fix this bug, you must subclass UIButton so that you can override alignmentRectInsets. From my testing, you'll need to return a UIEdgeInsets with either a positive right offset or a positive left offset, depending on the button position. These numbers make no sense to me (at least one of them should be negative, according to common sense), but this is what actually works:
- (UIEdgeInsets)alignmentRectInsets {
UIEdgeInsets insets;
if (IF_ITS_A_LEFT_BUTTON) {
insets = UIEdgeInsetsMake(0, 9.0f, 0, 0);
}
else { // IF_ITS_A_RIGHT_BUTTON
insets = UIEdgeInsetsMake(0, 0, 0, 9.0f);
}
return insets;
}
Special thanks to #zev for suggesting I try adjusting alignmentRectInsets.

I have tried all of the answers above and nothing worked for me. And here is what works, if anyone would need this:
(No subclassing needed)
// Add your barButtonItem with custom image as the following
UIBarButtonItem *barButton = [[UIBarButtonItem alloc] initWithTitle:#"" style:UIBarButtonItemStyleBordered target:self action:#selector(categoryButtonPressed)];
// set your custom image
[barButton setImage:categoryImage];
// finally do the magic
barButton.imageInsets = UIEdgeInsetsMake(0.0, -20, 0, 0);
-Take a look at the result.
Notice the space to the left button and from the right button (the
right button has the default behavior)

Ok, I went the other "direction". I made everything line up properly via Storyboard with iOS 7 (assuming this is how it will continue to work). And then using the describe sub-class approach, I sub-class UIButton with the following implementation.
- (UIEdgeInsets)alignmentRectInsets {
UIEdgeInsets insets;
if (SYSTEM_VERSION_LESS_THAN(#"7.0")) {
if ([self isLeftButton]) {
insets = UIEdgeInsetsMake(0, -10, 0, 0);
} else {
insets = UIEdgeInsetsMake(0, 0, 0, -10);
}
} else {
insets = UIEdgeInsetsZero;
}
return insets;
}
- (BOOL)isLeftButton {
return self.frame.origin.x < (self.superview.frame.size.width / 2);
}
So this code only runs if the device is pre-iOS 7.
Thanks for the insight #jaredsinclair!

#jaredsinclair
Here is a look at my code.
// Create the tweetly button that will show settings
self.tweetlyDisplay = [NavButton buttonWithType:UIButtonTypeCustom];
[self.tweetlyDisplay setFrame:CGRectMake(0, 0, 90, 44)];
[self.tweetlyDisplay setBackgroundColor:[UIColor clearColor]];
[self.tweetlyDisplay setBackgroundImage:[UIImage imageNamed:#"settings.png"] forState:UIControlStateNormal];
[self.tweetlyDisplay setAdjustsImageWhenHighlighted:NO];
[self.tweetlyDisplay addTarget:self action:#selector(tweetlyPressed:) forControlEvents:UIControlEventTouchUpInside];
// Add the Tweetly button as the left bar button item
// This had a glitch that moves the image to the right somewhat
UIBarButtonItem *leftBarButton = [[UIBarButtonItem alloc] initWithCustomView:self.tweetlyDisplay];
self.navigationItem.leftBarButtonItem = leftBarButton;
See anything that isnt right?
Here is the result. The second image is not so visible because I had to time taking the screenshot and it is still in transition, but you can clearly see how it is improperly offset.
Good normal image:
Bad Offset Image:
After about a half second, the image then snaps back to the original image location.
Here is my code for the NavButton.h and .m:
/**********************************************
NavButton.h
**********************************************/
#import <UIKit/UIKit.h>
#interface NavButton : UIButton
#end
/**********************************************
NavButton.m
**********************************************/
#import "NavButton.h"
#implementation NavButton {
int imageHeight;
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
imageHeight = 44;
}
return self;
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
// Drawing code
}
*/
- (UIEdgeInsets)alignmentRectInsets {
UIEdgeInsets insets;
if ([self isLeftButton]) {
insets = UIEdgeInsetsMake(0, 9.0f, 0, 0);
}
else { // IF ITS A RIGHT BUTTON
insets = UIEdgeInsetsMake(0, 0, 0, 9.0f);
}
return insets;
}
- (BOOL)isLeftButton {
return self.frame.origin.x < (self.superview.frame.size.width / 2);
}
// THIS IS THE TRICK. We make the height of the background rect match the image.
-(CGRect)backgroundRectForBounds:(CGRect)bounds
{
CGRect bgRect = bounds;
bgRect.origin.y = (bounds.size.height - imageHeight)/2.0f;
bgRect.size.height = imageHeight;
return bgRect;
}
#end

A better solution without jumping buttons arround can be found here:
Custom UIBarButtonItem alignment off with iOS7

Not a huge fan of subclassing UIButton or method swizzling from Marius's answer: https://stackoverflow.com/a/19317105/287403
I just used a simple wrapper, and moved the button's frame's x in a negative direction until I found the correct positioning. Button tapping appears to be fine as well (although you could extend the width of the button to match the negative offset if you needed).
Here's the code I use to generate a new back button:
- (UIBarButtonItem *) newBackButton {
// a macro for the weak strong dance
#weakify(self);
UIButton *backButton = [[UIButton alloc] init];
[backButton setTitle:#"Back" forState:UIControlStateNormal];
backButton.titleLabel.font = [UIFont systemFontOfSize:17];
CGFloat width = [#"Back" boundingRectWithSize:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX) options:NSStringDrawingUsesFontLeading|NSStringDrawingUsesLineFragmentOrigin attributes:#{NSFontAttributeName: [UIFont systemFontOfSize:17]} context:nil].size.width + 27;
backButton.frame = CGRectMake(-17.5, 0, width + 17.5, 44);
[backButton setImage:[UIImage imageNamed:#"UINavigationBarBackIndicatorDefault"] forState:UIControlStateNormal];
[backButton setTitleEdgeInsets:UIEdgeInsetsMake(0, 14, 0, 0)];
[backButton setTitleColor:mTCOrangeColor forState:UIControlStateNormal];
backButton.contentEdgeInsets = UIEdgeInsetsZero;
backButton.imageEdgeInsets = UIEdgeInsetsZero;
[backButton addEventHandler:^(id sender) {
// a macro for the weak strong dance
#strongify(self);
// do stuff here
} forControlEvents:UIControlEventTouchUpInside];
UIView *buttonWrapper = [[UIView alloc] initWithFrame:CGRectMake(0, 0, width, 44)];
[buttonWrapper addSubview:backButton];
return [[UIBarButtonItem alloc] initWithCustomView:buttonWrapper];
}

I would like to add to #jaredsinclair confirmed answer the below override methods to those who have text and/or image in their navigation button, otherwise the text and the image won't be aligned (only the background image):
- (UIEdgeInsets) titleEdgeInsets {
if(SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(#"7.0"))
{
if (IF_ITS_A_LEFT_BUTTON) {
{
return UIEdgeInsetsMake(0, 0, 0, 9.0f);
}
else
{ // IF_ITS_A_RIGHT_BUTTON
return UIEdgeInsetsMake(0, 9.0f, 0, 0);
}
}
return [super titleEdgeInsets];
}
- (UIEdgeInsets)imageEdgeInsets {
if(SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(#"7.0"))
{
if (IF_ITS_A_LEFT_BUTTON)
{
return UIEdgeInsetsMake(0, 0, 0, 9.0f);
}
else
{ // IF_ITS_A_RIGHT_BUTTON
return UIEdgeInsetsMake(0, 9.0f, 0, 0);
}
}
return [super imageEdgeInsets];
}
p.s. the macro is:
#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)

*Solution found, read on the end of the answer.*
#jaredsinclair
I have similar case as Kyle Begeman
This is the button
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.contentHorizontalAlignment = UIControlContentHorizontalAlignmentCenter;
}
return self;
}
- (UIEdgeInsets)alignmentRectInsets {
UIEdgeInsets insets;
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(#"7.0")){
if ([self isLeftButton]) {
insets = UIEdgeInsetsMake(0, 9.0f, 0, 0);
} else {
insets = UIEdgeInsetsMake(0, 0, 0, 9.0f);
}
}else{
insets = UIEdgeInsetsZero;
}
return insets;
}
- (BOOL)isLeftButton {
return self.frame.origin.x < (self.superview.frame.size.width / 2);
}
and I use it in this case
PBNavigationBarButton *dashboardButton = [PBNavigationBarButton buttonWithType:UIButtonTypeCustom];
UIImage *dashboardImage = [UIImage imageNamed:#"btn_dashboard.png"];
UIImage *dashboardImageHighlighted = [UIImage imageNamed:#"btn_dashboard_pressed.png"];
NSInteger halfImageWidth = ceilf([dashboardImage size].width/2.0f);
[dashboardButton setBackgroundImage:[dashboardImage stretchableImageWithLeftCapWidth:halfImageWidth topCapHeight:0] forState:UIControlStateNormal];
[dashboardButton setBackgroundImage:[dashboardImageHighlighted stretchableImageWithLeftCapWidth:halfImageWidth topCapHeight:0] forState:UIControlStateHighlighted];
[dashboardButton addTarget:target action:action forControlEvents:UIControlEventTouchUpInside];
[dashboardButton sizeToFit];
UIBarButtonItem *barButtonItem = [[UIBarButtonItem alloc] initWithCustomView:dashboardButton];
Everything looks great except that, when I go back to the previous VC, the button repositions itself. Like a small jump.
For me it does jump right away not after a second. And it happens after I go do the popVC from NavigationViewController.
Edit: The answer below, the swizzling method from Marius helped me as well.

I came up with a shortened Version of jaredsinclair's approach:
- (UIEdgeInsets)alignmentRectInsets {
return [self isLeftButton] ? UIEdgeInsetsMake(0, 9.0f, 0, 0) : UIEdgeInsetsMake(0, 0, 0, 9.0f);
}
- (BOOL)isLeftButton {
return self.frame.origin.x < (self.superview.frame.size.width / 2);
}
Works like a charm.

in ios7 you can just add a dummy barbuttonitem
for fixing left space you should add dummy as first, for right as last
example for left, you should add this after setting your original items or in viewdidload if you are setting buttons using storyboard.
NSMutableArray *buttons = [[NSMutableArray alloc] init];
UIBarButtonItem *spacerItem = [[UIBarButtonItem alloc] init];
[buttons addObject:spacerItem];
for(UIBarButtonItem *item in self.leftBarButtonItems){
[buttons addObject:item];
}
[self setLeftBarButtonItems:[NSArray arrayWithArray:buttons] animated:NO];

It just simply adjust imageEdgeInsets to fix this bug.
try this.
UIButton *actionBtn = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 40, 40)];
[actionBtn setImage:[UIImage imageNamed:#"arrow.png"] forState:UIControlStateNormal];
[actionBtn addTarget:self action:#selector(goToSetting) forControlEvents:UIControlEventTouchUpInside];
actionBtn.imageEdgeInsets = UIEdgeInsetsMake(0, 9, 0, -9);
UIBarButtonItem *profileBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:actionBtn];
self.topViewController.navigationItem.rightBarButtonItems = #[profileBarButtonItem];

Tested in iOS 11
public lazy var navigationBackBarButton : UIBarButtonItem = {
let backBarButtonIcon = <Image Literal here>
let backBarButton = UIBarButtonItem(image: backBarButtonIcon, style: .plain, target: self, action: #selector(self.backBarButtonTouched(_:)))
backBarButton.imageInsets = UIEdgeInsets(top: 0.0, left: -9.0, bottom: 0.0, right: 0.0)
return backBarButton
}()

2018, iOS 11+, Swift 4.x, this worked for me.
Combining the top answers:
Property
internal lazy var button_Favorite: UIButton = {
let button = UIButton(type: .custom)
button.setImage(.favNormal, for: .normal)
button.contentHorizontalAlignment = .right
button.imageEdgeInsets = UIEdgeInsetsMake(0, 0, 0, -9.0)
return button
}()
In viewDidLoad:
let barButton = UIBarButtonItem(customView: self.button_Favorite)
self.button_Favorite.frame = CGRect(x: 0, y: 0, width: 40.0, height: 44.0)
let negativeSpacer = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.fixedSpace, target: nil, action: nil)
negativeSpacer.width = -10.0
self.navigationItem.rightBarButtonItems = [negativeSpacer, barButton]

Change the following for your UIButton
Control -> Alignment -> Horizontal to Align Right

Related

Image change validation

I want to change the image in validation like if the text field is empty the image color is grey or if filled the circle is green color.
I have tried this in button action-
UIImageView *imgView = [[UIImageView alloc] initWithFrame:CGRectMake(10, 10, 100, 50)];
imgView.image = [UIImage imageNamed:#"a_image.png"];
imgView.contentMode = UIViewContentModeCenter;
Try this in your button action.
if (self.MyTextField.text.length!=0)
self.MyImageView.image=[UIImage imageNamed:#"redImage"];
else
self.MyImageView.image=[UIImage imageNamed:#"greenImage"];
If you want to change colour when user is typing in text field, use following delegate method of textfield to do any stuff.
-(void)textFieldDidBeginEditing:(UITextField *)textField;
UIImageView *imgView = [[UIImageView alloc] initWithFrame:CGRectMake(10, 10, 100, 50)];
if (textField.text.length == 0){
imgView.image = [UIImage imageNamed:#"firstImage.png"];
} else {
imgView.image = [UIImage imageNamed:#"secondImage.png"];
}
Should be pretty simple:
if (textField.text.length == 0){
//Do something if its empty
} else {
//Do something else if its not
}
EDIT: If you want to do this on the fly and not when they're done editing you could do something like:
- (void)viewDidLoad
{
[super viewDidLoad];
[self.textField addTarget:self action:#selector(textChanged) forControlEvents:UIControlEventEditingChanged];
}
- (void)textChanged
{
if (self.textField.text.length == 0) {
self.someVIew.backgroundColor = [UIColor grayColor];
} else if (self.textField.text.length > 5){
self.someVIew.backgroundColor = [UIColor redColor];
} else {
self.someVIew.backgroundColor = [UIColor greenColor];
}
}

Add UItextfield on button click

I'm new to Objective-c and i've reached an issue.
I'm trying to make a small scoreboard app, for learning more of the language.
Here is a small part of the app.
What i want it it to do is to add a new UItext field when you press the add button. So on button click it looks like this.
How can i add multiple UItextfields on button click in objective-c?
This is a very simple requirement. All you have to do is to create and add the new "Player Number" text field and "Fouls" label each time you tap the "Add" button. After this, move the "Add" button downwards and that's it. Here is some example code.
First of all, to keep things simple I created a UIViewController subclass to hold the "Player Number" text box and the "Fouls" label to make it easy to create them multiple times. Here is the class:
// The .h file
#import <UIKit/UIKit.h>
#interface LLPlayerViewController : UIViewController
// This is a very simple class with no public properties or methods
#end
// The .m file
#implementation LLPlayerViewController
-(void)loadView {
// Create the initial view
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 190, 25)];
self.view = view;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// PlayerNumber Text Field
UITextField *playerNumberField = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, 120, 20)];
playerNumberField.placeholder = #"PlayerNumber";
playerNumberField.borderStyle = UITextBorderStyleLine;
[self.view addSubview:playerNumberField];
// Fouls Label
UILabel *foulsLabel = [[UILabel alloc] initWithFrame:CGRectMake(135, 0, 50, 20)];
foulsLabel.text = #"0";
[self.view addSubview:foulsLabel];
}
#end
Then, in the main View Controller I create instances of that class and add them to the view each time I press the "Add" button. Here is some sample code:
// Don't forget to import the previous class
#import "LLPlayerViewController.h"
- (void)viewDidLoad
{
[super viewDidLoad];
// Inside viewDidLoad add the following code:
self.playersArray = [[NSMutableArray alloc] init];
// PLAYER Header
UILabel *playerLabel = [[UILabel alloc] initWithFrame:CGRectMake(15, 25, 80, 20)];
playerLabel.text = #"PLAYER";
[self.view addSubview:playerLabel];
// FOULS Header
UILabel *foulsLabel = [[UILabel alloc] initWithFrame:CGRectMake(145, 25, 80, 20)];
foulsLabel.text = #"FOULS";
[self.view addSubview:foulsLabel];
// Add player button
self.addPlayerButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
self.addPlayerButton.frame = CGRectMake(30, 60, 80, 30);
[self.addPlayerButton setTitle:#"Add" forState:UIControlStateNormal];
[self.addPlayerButton addTarget:self action:#selector(addPlayerTapped:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:self.addPlayerButton];
}
// This method will get called each time the user taps the "Add" button
-(void)addPlayerTapped:(id)sender {
NSLog(#"Adding Player");
self.yOffset = 60.0;
LLPlayerViewController *llVC = [[LLPlayerViewController alloc] init];
llVC.view.alpha = 0.0;
[self.playersArray addObject:llVC];
[self.view addSubview:llVC.view];
[self repositionFields];
}
// This method is responsible for repositioning the views
-(void)repositionFields {
// Reposition each view in the array
for (LLViewController *llVC in self.playersArray) {
CGRect frame = llVC.view.frame;
frame.origin.x = 15;
frame.origin.y = self.yOffset;
llVC.view.frame = frame;
self.yOffset += frame.size.height;
}
// Add some animations to make it look nice
[UIView animateWithDuration:0.3 animations:^{
CGRect frame = self.addPlayerButton.frame;
frame.origin.y = self.yOffset;
self.addPlayerButton.frame = frame;
} completion:^(BOOL finished) {
LLViewController *llVC = [self.playersArray lastObject];
llVC.view.alpha = 1.0;
}];
}
This code was tested on an iPad simulator but I believe it would also work on an iPhone. The code is straight-forward and self explanatory but let me know if you have any more questions.
Hope this helps!

iOS 7 BarButtonItem with image and title

I'm trying to figure out how can I achieve something like this:
This is a toolbar and I'd like to keep the button title text without having to create the whole image with icon AND text. How can I add the icon to the left of the UIBarButtonItem while keeping the text set with:
UIBarButtonItem *customBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:#"Button" style:UIBarButtonItemStyleBordered target:nil action:nil];
EDIT
This is what I achieved with the following code:
UIButton *customButton = [UIButton buttonWithType:UIButtonTypeCustom];
[customButton setImage:[UIImage imageNamed:#"Test"] forState:UIControlStateNormal];
[customButton setTitle:#"Button" forState:UIControlStateNormal];
customButton.titleEdgeInsets = UIEdgeInsetsMake(0, 5, 0, 0);
[customButton sizeToFit];
UIBarButtonItem *customBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:customButton];
Two issues here: the first is the size of the UIButton and the second is that, as you can see from the gif, the button doesn't animate properly when I tap inside the button and when I release the tap. Any ideas?
You can embed a UIButton as a custom view inside your UIBarButtonItem. You can create your UIButton however you want, including with an image and text, using -setImage:forState: and -setTitle:forState:.
UIButton* customButton = [UIButton buttonWithType:UIButtonTypeCustom];
[customButton setImage:[UIImage imageNamed:#"image"] forState:UIControlStateNormal];
[customButton setTitle:#"Button" forState:UIControlStateNormal];
[customButton sizeToFit];
UIBarButtonItem* customBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:customButton];
self.navigationItem.leftBarButtonItem = customBarButtonItem; // or self.navigationItem.rightBarButtonItem
Note that when doing this, you'll need to attach your IBAction methods to customButton, not to customBarButtonItem.
For more info, see the documentation for initWithCustomView:.
You can also do all of this in interface builder just by dragging a UIButton to the left bar button/right bar button slot on a navigation bar.
I was not satisfied with all solutions found on stack overflow becauseI like how UIBarButtonItenm convert images so they are displayed correctly and they can be changed with tint colour. So i discovered this solution that I can use nicely with not much code... End effect is similar to toolbar in facebook and other current apps...
When you change tint colour of view contained in UIBArButtonItem colours of image and title change accordingly, really cool , I do not have to create special images at all.
This code is called from each button like that Competition Button, and also Button is set as referencing outlet...
- (void)selectButton:(UIButton *)sender {
_competitionButton.superview.tintColor = [UIColor lightGrayColor];
_usersButton.superview.tintColor = [UIColor lightGrayColor];
_managementButton.superview.tintColor = [UIColor lightGrayColor];
sender.superview.tintColor = [UIColor whiteColor];
}
- (IBAction)onCompetitionButtonClick:(UIButton *)sender {
[self selectButton:sender];
[_scrollView scrollToPage:0 of:3];
}
In Button like that titled Competition there is only title and in bar button item there is image... This is how I have setup it and it works , maybe there are also other possibilities..
So I managed to do my solution programatically... Some users where complaining it does not work ... So how to create UTToolbar with image and item so that you can change color of button by tint and it magically alters image as well as title label ? My UI editor solution works for sure...
In this code can clarify things more and you can do it programatically either...
- (void)loadToolbar {
NSMutableArray *items = NSMutableArray.new;
[items add:[UIBarButtonItem createWithItem:UIBarButtonSystemItemFlexibleSpace :nil :nil]];
for (uint pageIndex = 0; pageIndex < _controllers.count; ++pageIndex) {
UIView *itemView = [UIView.alloc initWithFrame:CGRectMake(0, 0, 100, 44)];
itemView.backgroundColor = [UIColor clearColor];
itemView.tintColor = [UIColor orangeColor];
[itemView addSubview:[self createToolbarForItemView:pageIndex]];
[itemView addSubview:[self createButtonForItemView:pageIndex]];
UIBarButtonItem *item = [UIBarButtonItem.alloc initWithCustomView:itemView];
item.style = UIBarButtonItemStylePlain;
[items add:item];
[items add:[UIBarButtonItem createWithItem:UIBarButtonSystemItemFlexibleSpace :nil :nil]];
}
[_toolbar setItems:items];
}
- (UIButton *)createButtonForItemView:(uint)pageIndex {
UIButton *itemButton = [UIButton buttonWithType:UIButtonTypeSystem];
itemButton.frame = CGRectMake(0, 0, 100, 44);
itemButton.titleLabel.font = [UIFont systemFontOfSize:11];
itemButton.contentEdgeInsets = UIEdgeInsetsMake(30, 0, 0, 0);
itemButton.text = [_controllers[pageIndex] tabTitle];
itemButton.touchUp = ^(UIButton *sender) {
for (UIButton *button in _buttons) button.superview.tintColor = UI.toolBarInactiveButtonTextColor;
sender.superview.tintColor = UI.toolBarActiveButtonTextColor;
[self showPage:pageIndex];
};
[_buttons add:itemButton];
return itemButton;
}
- (UIToolbar *)createToolbarForItemView:(uint)pageIndex {
UIToolbar *itemToolbar = [UIToolbar.alloc initWithFrame:CGRectMake(0, 0, 100, 44)];
itemToolbar.tintColor = nil;
itemToolbar.barStyle = _toolbar.barStyle;
itemToolbar.translucent = _toolbar.translucent;
UIBarButtonItem *imageItem = [_controllers[pageIndex] tabImageItem];
imageItem.style = UIBarButtonItemStylePlain;
imageItem.tintColor = nil;
imageItem.imageInsets = UIEdgeInsetsMake(-10, 0, 0, 0);
itemToolbar.items = #[
[UIBarButtonItem createWithItem:UIBarButtonSystemItemFlexibleSpace :nil :nil],
imageItem,
[UIBarButtonItem createWithItem:UIBarButtonSystemItemFlexibleSpace :nil :nil]
];
return itemToolbar;
}
I solved this problem as follows:
[Button **setTitleColor**:[UIColor colorWithRed:129/255.0f green:124/255.0f blue:110/255.0f alpha:1.0f] forState:**UIControlStateHighlighted**];

Rotating a full screen UIImageView

I have two images:
Help-Portrait.png (320 x 480)
Help-Landscape.png (480 x 320)
When a user clicks the help button on any view, they need to be presented with the correct image, which should also rotate when the device does. I have tried adding the imageView to both the window, and the navigation controller view.
For some reason I am having issues with this.
Could anyone shed light on what I am doing wrong?
UIImage *image = nil;
CGRect frame;
if (UIInterfaceOrientationIsPortrait([[UIApplication sharedApplication] statusBarOrientation])) {
image = [UIImage imageNamed:#"Help-Portrait.png"];
frame = CGRectMake(0, 0, 320, 480);
} else {
image = [UIImage imageNamed:#"Help-Landscape.png"];
frame = CGRectMake(0, 0, 480, 320);
}
if (!helpImageView) {
helpImageView = [[UIImageView alloc] initWithFrame:frame];
helpImageView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
helpImageView.image = image;
}
[[UIApplication sharedApplication] setStatusBarHidden:YES];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(helpImageTapped:)];
helpImageView.userInteractionEnabled = YES;
[helpImageView addGestureRecognizer:tap];
[self.view addSubview:helpImageView];
[tap release];
willRotateToInterfaceOrientation:
if(helpImageView) {
[(id)[UIApplication sharedApplication] setStatusBarHidden:NO animated:YES];
if (UIInterfaceOrientationIsPortrait(toInterfaceOrientation)) {
helpImageView.image = [UIImage imageNamed:#"Help-Portrait.png"];
} else {
helpImageView.image = [UIImage imageNamed:#"Help-Landscape.png"];
}
}
When you rotate the device the image and the frame don't change, and you end up with two thirds of the portrait image displayed on the left part of the screen.
What I want is it for it to show the correct image for the orientation, the right way up. Also I would like animation for the image rotation, but thats a side issue
The place where you need to adjust your button image is in your ViewController's shouldAutorotateToInterfaceOrientation method (documentation linked for you).
Do something like:
- (BOOL)shouldAutorotateToInterfaceOrientation: (UIInterfaceOrientation)interfaceOrientation
{
UIImage *image = NULL;
if (UIInterfaceOrientationIsPortrait(interfaceOrientation))
{
image = [UIImage imageNamed:#"Help-Portrait.png"];
} else {
image = [UIImage imageNamed:#"Help-Landscape.png"];
}
[yourButton setImage: image forState: UIControlStateNormal]
return YES;
}
Michael Dautermann's answer looks to have almost all the answer, but I'm opposed to using shouldAutorotateToInterfaceOrientation. This method is designed only to determine if a rotation should or should not occur, nothing else.
You should use either didRotateFromInterfaceOrientation: willAnimateRotationToInterfaceOrientation:duration instead.
didRotateFromInterfaceOrientation: - interfaceOrientation is already set on your UIViewController so you can get the current orientation. In this case the rotation animation is already complete.
willAnimateRotationToInterfaceOrientation:duration - The benefit of this method is execution time. You are inside the rotation animation so you won't have the less than pretty effects which happens when you change UI either after the rotation animation completes.
Got it working, with this code:
- (void)showHelpImage {
NSString *imageName = #"Help_Portrait.png";
CGRect imageFrame = CGRectMake(0, 0, 320, 480);
helpImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:imageName]];
helpImageView.frame = imageFrame;
[self.view addSubview:helpImageView];
[self updateHelpImageForOrientation:self.interfaceOrientation];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(helpImageTapped:)];
helpImageView.userInteractionEnabled = YES;
[helpImageView addGestureRecognizer:tap];
[self.view addSubview:helpImageView];
[tap release];
}
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
[self updateHelpImageForOrientation:toInterfaceOrientation];
}
- (void)updateHelpImageForOrientation:(UIInterfaceOrientation)orientation {
NSString *imageName = nil;
CGRect imageFrame = helpImageView.frame;
if (orientation == UIInterfaceOrientationPortrait || orientation == UIInterfaceOrientationPortraitUpsideDown) {
imageName = #"Help_Portrait.png";
imageFrame = CGRectMake( 0, 0, 320, 480);
} else if (orientation == UIInterfaceOrientationLandscapeLeft || orientation == UIInterfaceOrientationLandscapeRight) {
imageName = #"Help_Landscape.png";
imageFrame = CGRectMake( 0, 0, 480, 320);
}
helpImageView.image = [UIImage imageNamed:imageName];
helpImageView.frame = imageFrame;
}
Got the idea from:
http://www.dobervich.com/2010/10/22/fade-out-default-ipad-app-image-with-proper-orientation/

Objective C - Help with changing background color when UIButton is pressed

I am new to programing and any help is appreciated. I am trying to change the background color of a button once it has been pressed. I have tried setBackgroundColor without success. I am not sure that it is compatible with UIButton. Is there any way to programatically accomplish such a task? All thoughts and suggestions are appreciated.
I woudl suggest creating a simple image that contains the background color you want and setting that via the existing methods in the UIButton. (check Wrights Answer for the doc link).
UIButton* button = [UIButton buttonWithType:UIButtonTypeCustom];
NSString* fileLocation = [[NSBundle mainBundle] pathForResource:#"buttonBG" ofType:#"png"];
UIImage* bgImage = [UIImage imageWithContentsOfFile:fileLocation];
if (bgImage != nil) { // check if the image was actually set
[button setBackgroundImage:bgImage forState:UIControlStateHighlighted];
} else {
NSLog(#"Error trying to read the background image");
}
That should do the trick. There might be an even better way to create the necessary image on the fly, but that's stuff I'm not firm in.
[edit: a bit more verbose code ]
Assuming you have an unadorned custom button with a title of "On" for the normal state:
- (IBAction) toggleButtonState {
if ([toggleButton titleForState:UIControlStateNormal] == #"On") {
[toggleButton setTitle: #"Off" forState:UIControlStateNormal];
[toggleButton setBackgroundColor:[UIColor redColor]];
}
else {
[toggleButton setTitle: #"On" forState:UIControlStateNormal];
[toggleButton setBackgroundColor:[UIColor greenColor]];
}
}
All the other buttons have an image placed in front of the view, so at most you'll see the corners change if the image doesn't completely fill the space.
I'd also suggest using an image, but for learning purposes, this will work.
Ive just been having the same issue and ended up using a UIButton subclass to tackle the issue. I used gradients simply because it looked a bit better if you have no need for them you can simply remove them. I have explained the process I used and included the full code at the bottom of the post.
Firstly add properties for the layers.I created two layers one for the base gradient and one for a gloss to add a little bit of style.
#interface gradientButton()
#property (nonatomic, strong) CAGradientLayer* gradientLayer;
#property (nonatomic, strong) CAGradientLayer* glossyLayer;
#end
Then either in -(void)awakeFromNib or in -(id)initWithFrame:(CGRect)frame
,depending on if you will load from storyboard or code respectively, configure the gradients and add the layers, round your corners off and customize the font highlight color.
-(void)awakeFromNib
{
_gradientLayer = [[CAGradientLayer alloc] init];
_gradientLayer.bounds = self.bounds;
_gradientLayer.position = CGPointMake(self.bounds.size.width/2, self.bounds.size.height/2);
[self.layer insertSublayer:_gradientLayer atIndex:0];
self.layer.cornerRadius = 5.0f;
self.layer.masksToBounds = YES;
self.layer.borderWidth = 1.0f;
_glossyLayer = [[CAGradientLayer alloc] init];
_glossyLayer.bounds = CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height/2);
_glossyLayer.position = CGPointMake(self.bounds.size.width/2, self.bounds.size.height/4);
[self.layer addSublayer:_glossyLayer];
[self setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[self setTitleColor:[UIColor yellowColor] forState:UIControlStateHighlighted];
}
Next, override - (void)drawRect:(CGRect)rect to apply your layers and define your colors.
#define GRADIENT_TOP [UIColor colorWithRed:38.0/255.0 green:78.0/255.0 blue:54.0/255.0 alpha:1]
#define GRADIENT_BOTTOM [UIColor colorWithRed:44.0/255.0 green:71.0/255.0 blue:56.0/255.0 alpha:1]
#define GLOSS_TOP [UIColor colorWithRed:0.70f green:0.70f blue:0.70f alpha:0.95f]
#define GLOSS_BOTTOM [UIColor colorWithRed:0.70f green:0.70f blue:0.70f alpha:0.35f]
#define GRADIENT_SELECTED_TOP [UIColor colorWithRed:138.0/255.0 green:178.0/255.0 blue:154.0/255.0 alpha:1]
#define GRADIENT_SELECTED_BOTTOM [UIColor colorWithRed:114.0/255.0 green:171.0/255.0 blue:156.0/255.0 alpha:1]
- (void)drawRect:(CGRect)rect
{
[_gradientLayer setColors:#[(id)[GRADIENT_TOP CGColor],(id)[GRADIENT_BOTTOM CGColor]]];
[_glossyLayer setColors:#[(id)[GLOSS_TOP CGColor], (id)[GLOSS_BOTTOM CGColor]]];
[super drawRect:rect];
}
Finally, and the bit we've all been waiting for, override -(void)setHighlighted:(BOOL)highlighted{
so we can apply the highlight effect were looking for.
-(void)setHighlighted:(BOOL)highlighted{
[super setHighlighted:highlighted];
if(highlighted)
[_gradientLayer setColors:#[(id)[GRADIENT_SELECTED_TOP CGColor],(id)[GRADIENT_SELECTED_BOTTOM CGColor]]];
else
[_gradientLayer setColors:#[(id)[GRADIENT_TOP CGColor],(id)[GRADIENT_BOTTOM CGColor]]];
}
There we have it, now just drag out a UIButton modify the class and your all good. Heres the full Implementation so you can copy it straight out. http://pastebin.com/nUVeujyp
Check out the UIButton Class Reference.
Regular UIButtons do not have the backgroundColor option.
My suggestion would to use the UISegmentedControl, which has the tinColor option.
I have created a subclass which get background color and creates an UIImage for each state.
For me it's more useful a subclass instead a category, so that's up to you.
#implementation ROCRoundColorButton
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
UIColor *darkColor;
darkColor = [self darkColorFromBackgroundColor];
[self setBackgroundImage:[self imageWithColor:self.backgroundColor withSize:self.frame.size] forState:UIControlStateNormal];
[self setBackgroundImage:[self imageWithColor:darkColor withSize:self.frame.size] forState:UIControlStateSelected];
}
return self;
}
-(id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
UIColor *darkColor;
darkColor = [self darkColorFromBackgroundColor];
[self setBackgroundImage:[self imageWithColor:self.backgroundColor withSize:self.frame.size] forState:UIControlStateNormal];
[self setBackgroundImage:[self imageWithColor:darkColor withSize:self.frame.size] forState:UIControlStateSelected];
}
return self;
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
// Drawing code
}
*/
#pragma mark -
#pragma mark Private methods
- (UIColor *)darkColorFromBackgroundColor
{
const float* components = CGColorGetComponents( self.backgroundColor.CGColor );
CGFloat red = components[0];
CGFloat green = components[1];
CGFloat blue = components[2];
CGFloat alpha = components[3];
if (red > 0) {
red -= 0.1;
}
if (green > 0) {
green -= 0.1;
}
if (blue > 0) {
blue -= 0.1;
}
UIColor *darkColor = [UIColor colorWithRed:red green:green blue:blue alpha:alpha];
return darkColor;
}
- (UIImage *)imageWithColor:(UIColor *)color withSize:(CGSize)size
{
UIGraphicsBeginImageContext(size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [color CGColor]);
//CGContextFillRect(context, CGRectMake(0, 0, size.width, size.height));
UIBezierPath *roundedRect = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, size.width, size.height) cornerRadius:5];
[roundedRect fillWithBlendMode: kCGBlendModeNormal alpha:1.0f];
[color setFill];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
In fact, you can use it in the storyboard, changing the class and setting de background color in the view.