How to properly position the back button in iOS7 - objective-c

I used this code to use a custom image as the back button in the whole app.
[[UINavigationBar appearance] setBackIndicatorImage:[UIImage imageNamed:#"back"]];
[[UINavigationBar appearance] setBackIndicatorTransitionMaskImage:[UIImage imageNamed:#"back"]];
The image dimensions are 30 x 30.
The code adds the image as the back button but the position is not the correct, as you can see in the following image:
Any ideas on how to properly position the image without modifying its dimensions (at least the visual part of the image (circle + arrow))?
EDIT:
I don't want to use a custom back button because that forces me to disable the swipe/back-gesture in iOS7

EDIT
I think I might have found the trick (in iOS 7 Design Resource -- UIKit User Interface Catalog.)
Under Bar Button Items
Note that a bar button image will be automatically rendered as a template image within a navigation bar, unless you explicitly set its rendering mode to UIImageRenderingModeAlwaysOriginal. For more information, see Template Images.
Under Template Images they have some code to specify the UIImageRenderingMode.
UIImage *myImage = [UIImage imageNamed:#"back"];
UIImage *backButtonImage = [myImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
// now use the new backButtomImage
[[UINavigationBar appearance] setBackIndicatorImage:backButtonImage];
[[UINavigationBar appearance] setBackIndicatorTransitionMaskImage:backButtonImage];
Try creating the UIImage with alignment insets and then set the Back Indicator image.
UIEdgeInsets insets = UIEdgeInsetsMake(10, 0, 0, 0); // or (0, 0, -10.0, 0)
UIImage *alignedImage = [[UIImage imageNamed:#"back"] imageWithAlignmentRectInsets:insets];
[[UINavigationBar appearance] setBackIndicatorImage:alignedImage];
[[UINavigationBar appearance] setBackIndicatorTransitionMaskImage:alignedImage];
You might also try adjusting the position of the UINavigationBar title text
[[UINavigationBar appearance] setTitleVerticalPositionAdjustment:(CGFloat)adjustment forBarMetrics:(UIBarMetrics)barMetrics];

Well just follow one of the suggestions to fix the layout and lose the iOS 7 "back gesture", and then fix it with a UIScreenEdgePanGestureRecognizer!
A UIScreenEdgePanGestureRecognizer looks for panning (dragging) gestures that start near an edge of the screen. The system uses screen edge gestures in some cases to initiate view controller transitions. You can use this class to replicate the same gesture behavior for your own actions.

PLEASE SEE EDIT BELOW!!!
I created a custom back button in iOS7 not too long ago. Mine has an arrow and the word back on it. I do think pawan's suggestion is a good start. To create the back button with your custom image you can use,
UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithTitle:#"Back" style:UIBarButtonItemStylePlain target:self action:#selector(backButtonClicked)];
[backButton setBackgroundImage:finalImage forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
[backButton setTitlePositionAdjustment:UIOffsetMake(-20, 0) forBarMetrics:UIBarMetricsDefault];
self.navigationItem.leftBarButtonItem = backButton;
My image finalImage is a composite of two different images, but you can just use your "back" image. But I think that is where the problem lies. My image was a composite, you might want to make a composite as well, but put a clear space above your back icon. I placed a clear space to the right of my icon to adjust it's spacing. Here is the code,
UIImage *arrow = [UIImage imageNamed:#"back.png"];
UIImage *wordSpace = [UIImage imageNamed:#"whiteSpace.png"];
CGSize size = CGSizeMake(arrow.size.width + wordSpace.size.width, arrow.size.height);
UIGraphicsBeginImageContext(size);
[arrow drawInRect:CGRectMake(0, 0, arrow.size.width, size.height)];
[wordSpace drawInRect:CGRectMake(arrow.size.width, 0, wordSpace.size.width, wordSpace.size.height)];
UIImage *finalImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
The Image wordSpace is a clear png that I made in photoshop so my new back button image was not stretched. You might want to place a clear png on top, to push the icon down a little. Make the size.height of it in photoshop for what you think the adjustment should be. You might need to futz with this a bit. And make sure to change up the CGSize so that it fits your icon and the clear space.
My word back was a bit off, so I looked at
[backButton setTitlePositionAdjustment:UIOffsetMake(-20, 0) forBarMetrics:UIBarMetricsDefault];
I had to play around with that line a bit to make it look as good as possible but it finally gave me what I wanted with the -20. I even adjusted the second variable which is 0 in mine, this moved the actual icon around. -5 put the icon down way to far, but its another option from the clear png.
Now to deal with the fact that you want it to be an actual back button. Look at the first line of code I posted. The action on the button is #selector(backButtonClicked). So all you need to do is make that method and you should be good to go!
- (void)backButtonClicked
{
NSLog(#"going back");
[self.navigationController popViewControllerAnimated:YES];
}
Hope this helps a bit.
EDIT*****
I was playing around with my code a little bit and found a better way to move the back icon. I just used a ship's wheel because I didn't have the same one that you did, but it will work the same.
Since you don't really want a title you can create the button with this code,
UIImage *image = [UIImage imageNamed:#"781-ships-wheel.png"];
UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithImage:image style:UIBarButtonItemStylePlain target:self action:#selector(backButtonClicked)];
Just change the 781 stuff with your icon's name. Then you can move it around with the following,
[backButton setImageInsets:UIEdgeInsetsMake(20, 0, -20, 0)];
Take a look at this picture.
This shows the icon down considerably, but I wanted to show you the idea. The numbers for the Edge insets are Top, Left, Bottom, and Right. Don't touch the left and right if you don't need to move it that way, change the top and bottom. Notice however, that if you need to move it down by 20 points like I did, (way too much) you need to offset in the negative for the bottom, or the icon will get compressed. This is what it looks like with all zero's.
So you can pretty much move it where ever you want, but you will still have to set up the #selector(backButtonClicked) to make it work like the real back button.

This is Swift 2 version.
The simplest way is like this. Put this code in AppDelegate.'
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
let navigationBarAppearace = UINavigationBar.appearance()
let image = UIImage(named: "back-btn")
navigationBarAppearace.backIndicatorImage = image
navigationBarAppearace.backIndicatorTransitionMaskImage = image
return true
}
if your back button has background colour, it may won't work correctly.
Add your icon to asset folder for each resolution like this:

You can try this
self.navigationItem.leftBarButtonItem.imageInsets = UIEdgeInsetsMake(0, 0, 10, 0);

The problem is that your image is too tall. To prove this, first try this code:
UIGraphicsBeginImageContextWithOptions(CGSizeMake(10,20), NO, 0);
CGContextFillRect(UIGraphicsGetCurrentContext(), CGRectMake(6,0,4,20));
UIImage* im = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
self.navbar.backIndicatorImage = im;
UIGraphicsBeginImageContextWithOptions(CGSizeMake(10,20), NO, 0);
UIImage* im2 = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
self.navbar.backIndicatorTransitionMaskImage = im2;
It looks fine. Now change the 20 to 30 in the two CGSizeMake calls:
UIGraphicsBeginImageContextWithOptions(CGSizeMake(10,30), NO, 0);
CGContextFillRect(UIGraphicsGetCurrentContext(), CGRectMake(6,0,4,20));
UIImage* im = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
self.navbar.backIndicatorImage = im;
UIGraphicsBeginImageContextWithOptions(CGSizeMake(10,30), NO, 0);
UIImage* im2 = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
self.navbar.backIndicatorTransitionMaskImage = im2;
The icon is now too high.
So just make your image 20 pixels tall and all will be well.

UIEdgeInsets insets = UIEdgeInsetsMake(0, 0, -2, 0); // or (2,0,0,0)
UIImage *backArrowImage = [[UIImage imageNamed:#"back"] imageWithAlignmentRectInsets:insets];
[[UINavigationBar appearance] setBackIndicatorImage:backArrowImage];
[[UINavigationBar appearance] setBackIndicatorTransitionMaskImage:backArrowImage];

Related

why is the screenshot taken in objective c blank white image?

I want to take screenshot of the actual screen as viewed by the user on the iphone/ipad then present option for the user to share/save it
I have code and tried several but each time all i get is either a blank white or black image. The image is the same size of the screen.
The screen actual background is plain white and has white buttons on it, i can never capture the buttons.
NSLog(#"Taking The Screenshot");
CALayer *layer = [[UIApplication sharedApplication].delegate.window.rootViewController.layer;
CGFloat scale = [UIScreen mainScreen].scale;
UIGraphicsBeginImageContextWithOptions(layer.frame.size, NO, scale);
[layer renderInContext:UIGraphicsGetCurrentContext()];
screengrab = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
//new code for sharing...
UIActivityViewController *shareVC = [[UIActivityViewController alloc] initWithActivityItems:#[screengrab] applicationActivities:nil];
It is because you now have scenes and the app delegate window no longer works.
One way to fix this, if self is a VC, is to change your layer line to
CALayer * layer = self.view.window.rootViewController.view.layer;

Can't get a screenshot of UIView to be displayed inside UIImageView

Here's my code to get the screenshot of the view:
if (NULL != UIGraphicsBeginImageContextWithOptions){
UIGraphicsBeginImageContextWithOptions(pagedScrollView.frame.size, NO, [[UIScreen mainScreen] scale]);
}else{
UIGraphicsBeginImageContext(pagedScrollView.frame.size);
}
[pagedScrollView.layer renderInContext:UIGraphicsGetCurrentContext()];
screenshotImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIImageView *imageView = [[UIImageView alloc] initWithImage:screenshotImage];
imageView.frame = CGRectMake(imageView.frame.origin.x, imageView.frame.origin.y, imageView.frame.size.width, imageView.frame.size.height);
imageView.backgroundColor = [UIColor greenColor];
[self.view addSubview:imageView];
I'm 100% sure I have the right frame and the right view - the size of UIImageView say about it - but the thing is I can't get my UIImage to be displayed on the UIImageView.
The UIImageView is added on the view and I see a green region on it - but no image inside it, what can be wrong?
I'm using iOS 6 and ARC.
Well, I just tested your code running on my view controller's view and the capture and display worked fine. This leads me to believe that pagedScrollView is probably nil, or has a zero frame.
Either way, you'll be creating an image from a blank context and then passing it to your image view resulting in there not being any visible image.
It turns out I've been taking a screenshot of a long UIScrollView and I was always taking its first page - it had a lot of pages in it and sometimes the layer had nothing in it. It also turns out that UIImage was never released and the memory can effectively store dozens of them.

Setting UIButton image results in blue button in iOS 7

On iOS 6 SDK I wrote the following lines of code to display an image inside a button:
NSURL *thumbURL2 = [NSURL URLWithString:#"http://example.com/thumbs/2.jpg"];
NSData *thumbData2 = [NSData dataWithContentsOfURL:thumbURL2];
UIImage *thumb2 = [UIImage imageWithData:thumbData2];
[btn2 setImage:thumb2 forState:UIControlStateNormal];
[self.view addSubview:btn2];
But now with Xcode 5 and iOS 7 this doesn't work. The button doesn't contain the image. The button is filled with blue color.
In iOS7 there is new button type called UIButtonTypeSystem NS_ENUM_AVAILABLE_IOS(7_0), // standard system button
Check your .xib file and change button type to Custom
To do this programmatically, add this line to the viewDidLoad:
[UIButton buttonWithType:UIButtonTypeSystem];
It seems iOS 7 is using the image provided just as an Alpha mask for displaying the button's tint color.
Changing the button type to UIButtonTypeCustom did the trick for me (thanks user716216!).
Setting the image as background doesn't always work if you already have a background image, as was my case.
Swift 3, 4, 5 :
let image = UIImage(named: "my-image")
myButton.setImage(image.withRenderingMode(.alwaysOriginal), for: .normal)
There's a good chance that the image is there and you just can't see it. Try changing the button's type to UIButtonTypeCustom. If that doesn't work, set the button's background color to [UIColor clearColor];
For swift:
let aButton = UIButton.buttonWithType(UIButtonType.Custom) as UIButton
The issue is the TintColor. By default, iOS throws a blue tint color over every button. You can get around it through 3 ways.
Change the tint color. [button setTintColor:[UIColor blackColor]];
This may color your image in ways you don't want it to.
As most other suggested, set the background image. [button setBackgroundImage:[UIImage...]];
Add an UIImageView to your button.
UIImageView * img = [[UIImageView alloc] initWithImage:[UIImage...]];
[button addSubView:img];
I had the same issue.
On my storyboard I had a button without any image.
I would then assign the image in the code.
IOS 7 came and I got a lot of blue images.
The resolution was simple yet confusing. If I assign any image on the storyboard and then change the image at run time it works fine.
You always must specify a starting image on the storyboard even if you are not going to use it.
This worked for me
[myButton1 setBackgroundImage:[UIImage imageNamed:#"phones.png"] forState:UIControlStateNormal];
Note:Remove front image before doing this.
Old thread, but I wanted to chime in because I just had the same problem. The issue was just that you are calling setImage when you should call setBackgroundImage.
In iOS 13 -- just set the Tint property to White, while keeping the type of the UIButton as Custom
None of the given solutions were working for me. If you do not set an initial image in Storyboard, you can still change the image of the button by using setBackgroundImage.
For your example, only a minor change is needed.
NSURL *thumbURL2 = [NSURL URLWithString:#"http://example.com/thumbs/2.jpg"];
NSData *thumbData2 = [NSData dataWithContentsOfURL:thumbURL2];
UIImage *thumb2 = [UIImage imageWithData:thumbData2];
[btn2 setBackgroundImage:thumb2 forState:UIControlStateNormal];
[self.view addSubview:btn2];
This Problem is called blue color problem of the button in xcode.
When we make button by code the button shows the blue tint color by default.This can be solved byt assigning tint color to black or white accordingly to your cell's color.
The code is :
UIImage *closebtnimg = [UIImage imageNamed:#"icon_uncheck.png"];
UIImage *closebtnimg1 = [UIImage imageNamed:#"icon_checked.png"];
Custombutton *button = [Custombutton buttonWithType:UIButtonTypeRoundedRect];
[button setFrame:CGRectMake(52, 66, 25, 24)];
[button setBackgroundImage:closebtnimg forState:UIControlStateNormal];
[button setBackgroundImage:closebtnimg1 forState:UIControlStateSelected];
[button setTintColor:[UIColor whiteColor]];
[cell.contentView addSubview:button];
[button addTarget:self action:#selector(changeImage:) forControlEvents:UIControlEventTouchUpInside];
Using Xcode 9.2 none of the above solutions worked for what I was looking for.
I was looking for a solution that will let me set .normal and .selected UIControlState images inside the storyboard for their original rendering mode, but, inside the Swift file, no string literals should exist regarding the image names.
Basically, inside your code you will get the image you set inside your storyboard for .normal state and re-render it as .alwaysOriginal (Same for .selected state), then, you will set that image (which is now rendered as original and won't be affected by the tint) for the relevant state (.normal and .selected) of your UIButton.
Here it is:
// Get your .normal image (you set via your storyboard) and render it as original
let unselectedImage = yourButton.image(for: .normal)?.withRenderingMode(.alwaysOriginal)
// Set your normal image but this time rendered as original
yourButton.setImage(unselectedImage, for: .normal)
// Same for selected state
let selectedImage = yourButton.image(for: .selected)?.withRenderingMode(.alwaysOriginal)
yourButton.setImage(selectedImage, for: .selected)
This way you can set your button image states and if the image name will change, it won't affect your code.
making the tint color as clearcolor for all the four states(Default,Highlighted,selected,disabled) worked for me.
In Swift 4, initialize your UIButton and assign uyour image Data as follows:
let myButton = UIButton(type: .cutsom)
myButton.setImage(UIImage(data:myImageData), for: .normal)

UINavigationBar Back Button stretchable image too long

I am using the following code to customise the back button of our UINavigationBar. However, the image is stretched too far, resulting in an image like the following. Please can you tell me how to prevent this?
Thanks!
[[UIImage imageNamed:#"back_button"] stretchableImageWithLeftCapWidth:14 topCapHeight:0]
Original Back Button Image:
The minimum width of the UIBarButtonItem is largely determined by the size of the image you provide. You should export your back arrow image from your editor so that there is only 1 pixel of tileable image content in the middle column of the image, such that the left and right portions can be used as the left and right caps:
As per the image, it seems image is stretched correctly. Just check the back button frame. Also please verify, if there is no whitespace in end of the string "Profile".
stretchableImageWithLeftCapWidth:topCapHeight is deprecated in iOS 5.
This doesn't answer your question. It's just a hint.
Deprecated UIImage Methods
I have an image with a noise texture on it and wanted to do the same thing. I finally arrived at this solution which I believe does exactly what you want (at least in iOS 6):
UIBarButtonItem *backButton = [[UIBarButtonItem alloc] init];
UIImage *buttonBg = [[UIImage imageNamed:#"back-arrow.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 16, 0, 6)];
[backButton setBackButtonBackgroundImage:buttonBg forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
backButton.title = #"Back";
You can customize your edge insets to exclude portions that shouldn't stretch.

Change Round Rect Button background color on StateHighlighted

I'm trying to change the background color of a button when it's selected and don't want to use an image.
[mBtn setBackgroundColor:[UIColor grayColor] forState:UIControlStateHighlighted];
Any thoughts?
I'm replying to this old thread because it pops up consistently in searches for a solution to this problem and I have seen no solution elsewhere. It is truly annoying that setTintColor only applies to the highlighted state of a UIButton. Six months ago, it was equally annoying that it applied only to iOS 5, but that will hopefully be less of an issue going forward. With that in mind, I've drawn upon and combined a number of community suggestions to composite a general purpose solution to tinting a group of buttons in their normal state.
The method below accepts an NSArray of UIButtons and a set of color specifications as input. It applies the color specifications to one button using setTintColor, renders the result to a UIImage, and applies that image as the background image of the entire set of buttons. This avoids the need to create discrete image files for button colors. Also, it does so using a stretchable image so that it may work with a collection of buttons of different sizes (though note that it assumes the default corner rounding factors of UIButton). I hope you'll find it useful for iOS 5 targets.
- (void) setColorOfButtons:(NSArray*)buttons red:(float)red green:(float)green blue:(float)blue alpha:(float)alpha {
if (buttons.count == 0) {
return;
}
// get the first button
NSEnumerator* buttonEnum = [buttons objectEnumerator];
UIButton* button = (UIButton*)[buttonEnum nextObject];
// set the button's highlight color
[button setTintColor:[UIColor colorWithRed:red/255.9999f green:green/255.9999f blue:blue/255.9999f alpha:alpha]];
// clear any existing background image
[button setBackgroundImage:nil forState:UIControlStateNormal];
// place the button into highlighted state with no title
BOOL wasHighlighted = button.highlighted;
NSString* savedTitle = [button titleForState:UIControlStateNormal];
[button setTitle:nil forState:UIControlStateNormal];
[button setHighlighted:YES];
// render the highlighted state of the button into an image
UIGraphicsBeginImageContext(button.layer.frame.size);
CGContextRef graphicsContext = UIGraphicsGetCurrentContext();
[button.layer renderInContext:graphicsContext];
UIImage* image = UIGraphicsGetImageFromCurrentImageContext();
UIImage* stretchableImage = [image stretchableImageWithLeftCapWidth:12 topCapHeight:0];
UIGraphicsEndImageContext();
// restore the button's state and title
[button setHighlighted:wasHighlighted];
[button setTitle:savedTitle forState:UIControlStateNormal];
// set background image of all buttons
do {
[button setBackgroundImage:stretchableImage forState:UIControlStateNormal];
} while (button = (UIButton*)[buttonEnum nextObject]);
}
[mBtn setTintColor:[UIColor grayColor]];
This only effects the highlighted state, so I believe that this is what you're looking for.
You can also set it from Interface Builder, from the Highlight Tint drop-down menu.
Just for people that will land here like I did when searching for changing background colors for highlighted state...
I ended up with an UIButton subclass that has a property for backgroundHighlightColor and tracks highlighting through KVO. Here's the link to GitHub: SOHighlightButton
You should be able to adapt it to any other scenario if you need more / other properties ot the UIButton to change if highlighted.
There is no method like this, setBackgroundColor: forState:
Check documentation. you need to use image.