I am trying to accomplish the same look of my UISearchBar with a TextField within it, as in my iOS 6 app. I have tried to code it in several ways and not yet been successful. The problem is, I am not able to change the TextField's frame in any way since iOS 7. The result is, my TextField takes all the space in the NavigationBar and overrides the UIBarButtonItem (menu button) to the right. See pictures below:
iOS 6 code: This is how I coded it in iOS 6, where I could set the TextFields frame to whatever I liked!
UITextField *sbTextField = (UITextField *)[searchBar.subviews lastObject];
[sbTextField removeFromSuperview];
CGRect rect = searchBar.frame;
rect.size.height = 32;
rect.size.width = 210;
sbTextField.frame = rect;
[sbTextField setAutoresizingMask:UIViewAutoresizingFlexibleBottomMargin];
UIBarButtonItem *searchBarNavigationItem = [[UIBarButtonItem alloc] initWithCustomView:sbTextField];
[[self navigationItem] setLeftBarButtonItem:searchBarNavigationItem];
The result from the code above in iOS 7: ![iOS 7 look]
iOS 7 code: The difference in iOS 7, is that you need to use subViews in order to add the UITextField to the UISearchBar/UINavigationBar. By doing this I have not yet been able to change its frame. It currently overlaps the menuButton to the right which can be seen in the picture below this code...
UITextField* sbTextField;
CGRect rect = subView.frame;
rect.size.height = 32;
rect.size.width = 115;
for (UIView *subView in self.searchBar.subviews){
for (UIView *ndLeveSubView in subView.subviews){
if ([ndLeveSubView isKindOfClass:[UITextField class]])
{
sbTextField = (UITextField *)ndLeveSubView;
sbTextField.backgroundColor =[UIColor whiteColor];
UIBarButtonItem *searchBarNavigationItem = [[UIBarButtonItem alloc] initWithCustomView:sbTextField];
sbTextField.frame = rect;
self.navigationItem.leftBarButtonItem = searchBarNavigationItem;
self.navigationItem.rightBarButtonItem = menuButton;
[sbTextField removeFromSuperview];
break;
}
}
}
[self.searchBar reloadInputViews];
SO...Is it possible to change a subView's frame (TextField) in any way ? :(
EDIT
The answer is kinda lame. In order to make the code work in ios7 with a button to the right of the TextField, the TextField must be set as the titleView of the navigationBar. Which was not the case in ios 6. But there will be other glitches and it is not recommended to use TextField within searchBars in iOS7. Use searchDispalyController instead. Se my answer below
self.navigationItem.titleView = sbTextField;
You should not put a UITextField in the UINavigationBar in iOS 7, this widget is already provided by Apple.
In iOS 7, you can simply use a UISearchDisplayController with a UISearchBar, and set:
searchDisplayController.displaySearchBarInNavigationBar = YES
The search bar will appear in your UINavigationBar, and it will play nice with the other UIBarButtonItems without all the hacks and manual frame sizing in your original iOS 6 solution.
One thing to note - if you are going to add this to a project that still supports OSes older than iOS 7, you'll want to make sure that you put a check around the call or your app will crash when running on older OSes.
if([searchDisplayController respondsToSelector:#selector(displaysSearchBarInNavigationBar)])
{
searchDisplayController.displaysSearchBarInNavigationBar = YES;
}
See this section of the iOS 7 transition guide:
https://developer.apple.com/library/ios/documentation/userexperience/conceptual/TransitionGuide/Bars.html
In iOS 7, UISearchDisplayController includes the
displaysSearchBarInNavigationBar property, which you can use to put a
search bar in a navigation bar, similar to the one in Calendar on
iPhone:
One other note - you should consider migrating to AutoLayout going forward so you don't have to do all that tedious frame manipulation. Apple recommends it, and probably for good reason (future devices with larger screens...?)
in iOS 7 to access Text Field you have to reiterate on level more. Change your code like this
for (UIView *subView in self.searchBar.subviews){
for (UIView *ndLeveSubView in subView.subviews){
if ([ndLeveSubView isKindOfClass:[UITextField class]])
{
searchBarTextField = (UITextField *)ndLeveSubView;
break;
}
}
}
But best way to clear backgournd of UISearchBar and setting searchbar icon in text field is:
[searchBar setBackgroundImage:[[UIImage alloc] init] ];//if you want to remove background of uisearchbar
UIImage *image = [UIImage imageNamed: #"search_icon.png"];
[searchBar setImage:image forSearchBarIcon:UISearchBarIconSearch state:UIControlStateNormal];
Create a UIView *textFieldContainer with your target frame, add your textfield to that UIView and then add that textFieldContainer as a navigation item. i.e. your approach remains the same just the textfield comes inside a container and you play with that container.
Try this out i am not sure but this should work as in iOS 7 searchbar has subview and inside that subview there are two subviews one of which is UITextField
UIView *searchbarview = [searchBar.subviews objectAtIndex:0];
UITextField *sbTextField = (UITextField *)[searchbarview.subviews lastObject];
[sbTextField removeFromSuperview];
CGRect rect = searchBar.frame;
rect.size.height = 32;
rect.size.width = 210;
sbTextField.frame = rect;
[sbTextField setAutoresizingMask:UIViewAutoresizingFlexibleBottomMargin];
UIBarButtonItem *searchBarNavigationItem = [[UIBarButtonItem alloc] initWithCustomView:sbTextField];
[[self navigationItem] setLeftBarButtonItem:searchBarNavigationItem];
iOS6 & iOS7 compatible solution:
- (void)setTextFieldAsDelegate:(UIView *)inputView {
for (UIView *view in inputView.subviews) {
if ([view isKindOfClass:[UITextField class]]) {
searchBarTextField = (UITextField *)view;
searchBarTextField.delegate = self;
break;
} else {
[self setTextFieldAsDelegate:view];
}
}
}
Swift solution
for subView in searchBar.subviews{
for deeperView in subView.subviews{
if let searchField:UITextField = deeperView as? UITextField{
searchField.layer.borderWidth = 1.0
searchField.layer.borderColor = UIColor(red: 134/255, green: 14/255, blue: 75/255, alpha: 1).CGColor
searchField.layer.cornerRadius = 5.0
}
}
}
Thanx to spotdog13. I finally managed to make it work for iOS 7 properly in the following way:
#define TABLE_BOTTOM_MARGIN 5
#define SYSTEM_VERSION_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedSame)
#define SYSTEM_VERSION_GREATER_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedDescending)
#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedDescending)
#interface HomeViewController ()
#end
#implementation HomeViewController
#synthesize searchBar;
#synthesize searchResults;
- (void)viewDidLoad
{
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(#"7.0")) {
[searchBar sizeToFit]; // standard size
searchBar.delegate = self;
// Add search bar to navigation bar
self.navigationItem.titleView = searchBar;
}
}
- (BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar {
// Manually activate search mode
// Use animated=NO so we'll be able to immediately un-hide it again
[self.searchDisplayController setActive:YES animated:NO];
// Hand over control to UISearchDisplayController during the search
// searchBar.delegate = (id <UISearchBarDelegate>)self.searchDisplayController;
return YES;
}
#pragma mark <UISearchDisplayDelegate>
- (void) searchDisplayControllerDidBeginSearch:(UISearchDisplayController
// Un-hide the navigation bar that UISearchDisplayController hid
[self.navigationController setNavigationBarHidden:NO animated:NO];
}
- (void) searchDisplayControllerWillEndSearch:(UISearchDisplayController
*)controller {
searchBar = (UISearchBar *)self.navigationItem.titleView;
// Manually resign search mode
[searchBar resignFirstResponder];
// Take back control of the search bar
searchBar.delegate = self;
}
Related
Since iOS 7, a UITextView does not scroll automatically to the cursor as the user types text that flows to a new line. This issue is well documented on SO and elsewhere. For me, the issue is still present in iOS 7.1. What am I doing wrong?
I installed Xcode 5.1 and targeted iOS 7.1. I'm using Auto Layout.
Here's how I position the text view's content above the keyboard:
- (void)keyboardUp:(NSNotification *)notification
{
NSDictionary *info = [notification userInfo];
CGRect keyboardRect = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
keyboardRect = [self.view convertRect:keyboardRect fromView:nil];
UIEdgeInsets contentInset = self.textView.contentInset;
contentInset.bottom = keyboardRect.size.height;
self.textView.contentInset = contentInset;
}
What I have tried: I have tried many of the solutions posted to SO on this issue as it pertains to iOS 7. All of the solutions that I have tried do not seem to hold up well for text views displaying an attributed string. In the following three steps, I outline how the most up-voted answer on SO (https://stackoverflow.com/a/19277383/1239263) responds to the user tapping the return key for the first time.
(1.) The text view became the first responder in viewDidLoad. Scroll to the bottom of the text view where the cursor is located.
(2.) Before typing a single character, tap the return key on the keyboard. The caret disappears out of sight.
(3.) Tapping the return key again, however, seems to normalize the situation. (Note: deleting the latter new line, however, makes the caret disappear once again).
Improved solution's code for UITextView descendant class:
#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)
#define is_iOS7 SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(#"7.0")
#define is_iOS8 SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(#"8.0")
#implementation MyTextView {
BOOL settingText;
}
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(handleTextViewDidChangeNotification:) name:UITextViewTextDidChangeNotification object:self];
}
return self;
}
- (void)scrollToCaretInTextView:(UITextView *)textView animated:(BOOL)animated {
CGRect rect = [textView caretRectForPosition:textView.selectedTextRange.end];
rect.size.height += textView.textContainerInset.bottom;
[textView scrollRectToVisible:rect animated:animated];
}
- (void)handleTextViewDidChangeNotification:(NSNotification *)notification {
if (notification.object == self && is_iOS7 && !is_iOS8 && !settingText) {
UITextView *textView = self;
if ([textView.text hasSuffix:#"\n"]) {
[CATransaction setCompletionBlock:^{
[self scrollToCaretInTextView:textView animated:NO];
}];
} else {
[self scrollToCaretInTextView:textView animated:NO];
}
}
}
- (void)setText:(NSString *)text {
settingText = YES;
[super setText:text];
settingText = NO;
}
Note it doesn't work when Down key is pressed on Bluetooth keyboard.
A robust solution should hold up in the following situations:
(1.) a text view displaying an attributed string
(2.) a new line created by tapping the return key on the keyboard
(3.) a new line created by typing text that overflows to the next line
(4.) copy and paste text
(5.) a new line created by tapping the return key for the first time (see the 3 steps in the OP)
(6.) device rotation
(7.) some case I can't think of that you will...
To satisfy these requirements in iOS 7.1, it seems as though it's still necessary to manually scroll to the caret.
It's common to see solutions that manually scroll to the caret when the text view delegate method textViewDidChange: is called. However, I found that this technique did not satisfy situation #5 above. Even a call to layoutIfNeeded before scrolling to the caret didn't help. Instead, I had to scroll to the caret inside a CATransaction completion block:
// this seems to satisfy all of the requirements listed above–if you are targeting iOS 7.1
- (void)textViewDidChange:(UITextView *)textView
{
if ([textView.text hasSuffix:#"\n"]) {
[CATransaction setCompletionBlock:^{
[self scrollToCaretInTextView:textView animated:NO];
}];
} else {
[self scrollToCaretInTextView:textView animated:NO];
}
}
Why does this work? I have no idea. You'll have to ask an Apple engineer.
For completeness, here's all of the code related to my solution:
#import "ViewController.h"
#interface ViewController () <UITextViewDelegate>
#property (weak, nonatomic) IBOutlet UITextView *textView; // full-screen
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *string = #"All work and no play makes Jack a dull boy.\n\nAll work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy.";
NSAttributedString *attrString = [[NSAttributedString alloc] initWithString:string attributes:#{NSFontAttributeName: [UIFont fontWithName:#"Verdana" size:30.0]}];
self.textView.attributedText = attrString;
self.textView.delegate = self;
self.textView.backgroundColor = [UIColor yellowColor];
[self.textView becomeFirstResponder];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardIsUp:) name:UIKeyboardDidShowNotification object:nil];
}
// helper method
- (void)scrollToCaretInTextView:(UITextView *)textView animated:(BOOL)animated
{
CGRect rect = [textView caretRectForPosition:textView.selectedTextRange.end];
rect.size.height += textView.textContainerInset.bottom;
[textView scrollRectToVisible:rect animated:animated];
}
- (void)keyboardIsUp:(NSNotification *)notification
{
NSDictionary *info = [notification userInfo];
CGRect keyboardRect = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
keyboardRect = [self.view convertRect:keyboardRect fromView:nil];
UIEdgeInsets inset = self.textView.contentInset;
inset.bottom = keyboardRect.size.height;
self.textView.contentInset = inset;
self.textView.scrollIndicatorInsets = inset;
[self scrollToCaretInTextView:self.textView animated:YES];
}
- (void)textViewDidChange:(UITextView *)textView
{
if ([textView.text hasSuffix:#"\n"]) {
[CATransaction setCompletionBlock:^{
[self scrollToCaretInTextView:textView animated:NO];
}];
} else {
[self scrollToCaretInTextView:textView animated:NO];
}
}
#end
If you find a situation where this doesn't work, please let me know.
I solved it by getting the actual position of the caret and adjusting to it, here's my method:
- (void) alignTextView:(UITextView *)textView withAnimation:(BOOL)shouldAnimate {
// where the blinky caret is
CGRect caretRect = [textView caretRectForPosition:textView.selectedTextRange.start];
CGFloat offscreen = caretRect.origin.y + caretRect.size.height - (textView.contentOffset.y + textView.bounds.size.height - textView.contentInset.bottom - textView.contentInset.top);
CGPoint offsetP = textView.contentOffset;
offsetP.y += offscreen + 3; // 3 px -- margin puts caret 3 px above bottom
if (offsetP.y >= 0) {
if (shouldAnimate) {
[UIView animateWithDuration:0.2 animations:^{
[textView setContentOffset:offsetP];
}];
}
else {
[textView setContentOffset:offsetP];
}
}
}
If you only need to orient after the user presses return / enter, try:
- (void) textViewDidChange:(UITextView *)textView {
if ([textView.text hasSuffix:#"\n"]) {
[self alignTextView:textView withAnimation:NO];
}
}
Let me know if it works for you!
I can't find original source but it works on iOS7.1
- (void)textViewDidChangeSelection:(UITextView *)textView
{
if ([textView.text characterAtIndex:textView.text.length-1] != ' ') {
textView.text = [textView.text stringByAppendingString:#" "];
}
NSRange range0 = textView.selectedRange;
NSRange range = range0;
if (range0.location == textView.text.length) {
range = NSMakeRange(range0.location - 1, range0.length);
} else if (range0.length > 0 &&
range0.location + range0.length == textView.text.length) {
range = NSMakeRange(range0.location, range0.length - 1);
}
if (!NSEqualRanges(range, range0)) {
textView.selectedRange = range;
}
}
Some one have made a subclass that solves all scrolling0related issues in UITextView. The implementation couldn't be easier - switch UITextView with the subclass PSPDFTextView.
A post about it, showing what is fixed (With nice Gif animations) is here: Fixing UITextView on iOS 7
The git is here: PSPDFTextView
My app has a tableview controller that is presented as a form sheet.
On top portion of tableviewcontoller there is a UView and inside of that UIView there is a navigation bar and a searchbar.
Everything works fine older version of IOS but in IOS7 when user taps searchbar everything is messes up.
Normal:
When User starts to type:
After seach ends:
in.h
UITableViewController<UITextFieldDelegate,UISearchDisplayDelegate,UISearchBarDelegate>
#property (nonatomic,weak) IBOutlet UINavigationBar *topBar;
Tried few things but code doesnt seem to be changing anything, when put a breakpoint it enters to delegate methods though
in.m
//-(void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller {
// if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0) {
// CGRect statusBarFrame = self.topBar.frame;
// [UIView animateWithDuration:0.25 animations:^{
// for (UIView *subview in self.tableView.subviews)
// subview.transform = CGAffineTransformMakeTranslation(0, statusBarFrame.size.height+50);
// }];
// }
//}
//
//-(void)searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller {
// if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0) {
// [UIView animateWithDuration:0.25 animations:^{
// for (UIView *subview in self.tableView.subviews)
// subview.transform = CGAffineTransformIdentity;
// }];
// }
//}
- (void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller {
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0) {
CGRect frame = self.searchDisplayController.searchBar.frame;
frame.origin.y += self.topBar.frame.size.height;
self.searchDisplayController.searchBar.frame = frame;
}
}
- (void)searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller {
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0) {
CGRect statusBarFrame = self.topBar.frame;
CGRect frame = self.searchDisplayController.searchBar.frame;
frame.origin.y -= statusBarFrame.size.height;
self.searchDisplayController.searchBar.frame= frame;
}
}
#Additional Info
- (void)viewDidLoad
{
[super viewDidLoad];
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7)
{
//self.searchDisplayController.searchBar.searchBarStyle= UISearchBarStyleProminent;
self.edgesForExtendedLayout = UIRectEdgeNone;
//self.edgesForExtendedLayout = UIRectEdgeLeft | UIRectEdgeBottom | UIRectEdgeRight;
}
}
According to break points frame position and sizes are correct but they dont change self.searchDisplayController.searchBar.frame at all
I have also tried
-(void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller {
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0) {
[self.topBar setHidden:YES];
}
}
-(void)searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller {
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0) {
[self.searchDisplayController.searchBar removeFromSuperview];
CGRect frame = self.searchDisplayController.searchBar.frame;
frame.origin.y += self.topBar.frame.size.height;
self.searchDisplayController.searchBar.frame = frame;
[self.topView addSubview:self.searchDisplayController.searchBar];
[self.topView bringSubviewToFront:self.topBar];
[self.topBar setHidden:NO];
}
}
How can I solve this issue ?
try setting this value in your table view controller:
if ([self respondsToSelector:#selector(edgesForExtendedLayout)]){
self.edgesForExtendedLayout = UIRectEdgeNone;
}
And change you view hierarchy to have a view, and tableView, search bar and nag bar as it's subviews. Make your table view controller a view controller and make it as the data source and delegate.
Or don't use a searchBarDisplayController and just use a search bar with it's delegate methods:
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
I had a similar problem, I think searchDisplayController expands searchBar size to full tableHeaderView size (I really don't know why it doing this). I had searchbar and one custom toolbar (both 44px height) in tableHeaderView.
I've workarounded it with:
- (void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller {
self.tableView.tableHeaderView.frame = CGRectMake(0, 0, self.tableView.bounds.size.width, 44);
}
- (void)searchDisplayControllerDidEndSearch:(UISearchDisplayController *)controller {
self.tableView.tableHeaderView.frame = CGRectMake(0, 0, self.tableView.bounds.size.width, 88);
}
So I just setting tableHeaderView to the size of single UISearchBar when entering search and set it back when all animations complete. This solved my problem. Even animations still work (but they does not in the childViewController. No idea why)
I have an app with a support landscape and portrait mode. And I need the same behavior status bar like on iOS 6. What is the simplest way to do this?
I've tried the solutions in Stack Overflow question iOS 7 status bar back to iOS 6 style?, but it doesn't work. My subview depend on the view size, and my view doesn't stretch correctly. I don't want to update my XIB files; I simply want to add something that helps me. I don't know what it may be (hack or prayers).
You can try writing this in your ViewWillappear or DidAppear. Here we are shifting the view frame 20 pixels down.
CGRect frame = self.view.frame;
frame.origin.y = 20;
if (self.view.frame.size.height == 1024 ||
self.view.frame.size.height == 768)
{
frame.size.height -= 20;
}
self.view.frame = frame;
This will work, but however this is not a very good idea. You can also change the text colour of the status bar to light or dark depending on your app background by calling the following method if it helps.
-(UIStatusBarStyle)preferredStatusBarStyle
{
return UIStatusBarStyleLightContent; // For light status bar
return UIStatusBarStyleDefault // For Dark status bar
}
If you are on Xcode 5 and you are installing in iOS 7 then sorry, this will not happen (as far as I know).
If you want to see the status bar on iOS 7 like iOS 6 than open your project in Xcode 4.x.x and install in iOS 7. One problem with this approach I found is that sometimes Xcode 4.x.x doesn't recognise an iOS 7 device.
But if your Xcode 4.x.x can show your iOS 7 device then it will work.
The .api generated from Xcode 4.x.x will work in both iOS 6 and iOS 7, but you will not get extra space (of the status bar) on iOS 7 and the new look of keyboard, picker, switch, etc. But yes, you will get the new UIAlertView (I don't know why this is new and the other controls are old.)
I hope we will soon get a better solution in Xcode 5 for this.
UPDATE:
I found the way to run the app from Xcode 5 as Xcode 4. This is just matter of the base SDK.
If you want to built as Xcode 4 (iOS 6 SDK) from Xcode 5 then do the following.
Close Xcode 4 and 5.
In Xcode 4 Go to
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs
Here you will find iPhoneOS6.1.sdk. Copy this folder. And now go in Xcode 5 on the same path. In Xcode 5, you will find iPhoneOS7.0.sdk. Paste iPhoneOS6.1.sdk with it.
Now close the Finder and launch Xcode 5. Go to project target setting -> Build Setting and find Base SDK. Select iOS 6.1 as Base SDK. This will also work for 6.0. You just need to find iPhoneOS6.0.sdk.
Now you will see the device name twice in the run dropdown box. One for SDK 7.0 and one for SDK 6.1. So now you can run both ways with iOS 6 SDK and iOS 7 SDK.
I hope this will help someone.
I recently had to solve a similar problem, and I approached it in a slightly different way...
The approach was to use an extra view controller that acted as a container view controller for what was originally my rootViewController. First, i set up a container like this:
_containerView = [[UIView alloc] initWithFrame:[self containerFrame]];
_containerView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
_containerView.clipsToBounds = YES;
[self.view addSubview:_containerView];
[self.view setBackgroundColor:[UIColor blackColor]];
[UIApplication.sharedApplication setStatusBarStyle:UIStatusBarStyleLightContent animated:NO];
where the containerFrame was defined like this:
- (CGRect)containerFrame
{
if ([MyUtilityClass isSevenOrHigher])
{
CGFloat statusBarHeight = [MyUtility statusBarHeight]; //20.0f
return CGRectMake(0, statusBarHeight, self.view.bounds.size.width, self.view.bounds.size.height - statusBarHeight);
}
return self.view.bounds;
}
Finally, I added what was originally my rootViewController as a childViewController of the new one:
//Add the ChildViewController
self.childController.view.frame = self.containerView.bounds;
self.childController.view.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
[self addChildViewController:self.childController];
[self.containerView addSubview:self.childController.view];
[self.childController didMoveToParentViewController:self];
Things to note:
- Modal view controllers will still be presented in the iOS7 style, so I still have to account for that somehow.
Hope this helps someone!
This Guide helps me.
http://www.doubleencore.com/2013/09/developers-guide-to-the-ios-7-status-bar/
The most robust way to handle the 20 point size difference is Auto Layout.
If you aren’t using Auto Layout, Interface Builder provides you with tools to handle the screen size difference between iOS 7 and the older versions. When Auto Layout is turned off, you will notice an area in the sizing tab of the utility area (right pane) of Interface Builder that allows you to set iOS 6/7 Deltas.
1) It's a hack, but it works!
Use it if you doesn't use UIAlertView or KGStatusBar!!!
#import <objc/runtime.h>
#interface UIScreen (I_love_ios_7)
- (CGRect)bounds2;
- (CGRect)boundsForOrientation:(UIInterfaceOrientation)orientation;
#end
#implementation UIScreen (I_love_ios_7)
- (CGRect)bounds2
{
return [self boundsForOrientation:[[UIApplication sharedApplication] statusBarOrientation]];
}
- (CGRect)boundsForOrientation:(UIInterfaceOrientation)orientation
{
CGRect resultFrame = [self bounds2];
if(UIInterfaceOrientationIsLandscape(orientation))
resultFrame.size.width -= 20;
else
resultFrame.size.height -= 20;
return resultFrame;
}
#end
void Swizzle(Class c, SEL orig, SEL new)
{
Method origMethod = class_getInstanceMethod(c, orig);
Method newMethod = class_getInstanceMethod(c, new);
if(class_addMethod(c, orig, method_getImplementation(newMethod), method_getTypeEncoding(newMethod)))
class_replaceMethod(c, new, method_getImplementation(origMethod), method_getTypeEncoding(origMethod));
else
method_exchangeImplementations(origMethod, newMethod);
}
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7) {
Swizzle([UIScreen class], #selector(bounds2), #selector(bounds));
[application setStatusBarStyle:UIStatusBarStyleLightContent];
self.window.clipsToBounds =YES;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(applicationDidChangeStatusBarOrientation:)
name:UIApplicationWillChangeStatusBarOrientationNotification
object:nil];
NSDictionary* userInfo = #{UIApplicationStatusBarOrientationUserInfoKey : #([[UIApplication sharedApplication] statusBarOrientation])};
[[NSNotificationCenter defaultCenter] postNotificationName:UIApplicationWillChangeStatusBarOrientationNotification
object:nil
userInfo:userInfo];
}
return YES;
}
- (void)applicationDidChangeStatusBarOrientation:(NSNotification *)notification
{
UIInterfaceOrientation orientation = [[notification.userInfo objectForKey: UIApplicationStatusBarOrientationUserInfoKey] intValue];
CGSize size = [[UIScreen mainScreen] boundsForOrientation:orientation].size;
int w = size.width;
int h = size.height;
float statusHeight = 20.0;
switch(orientation){
case UIInterfaceOrientationPortrait:
self.window.frame = CGRectMake(0,statusHeight,w,h);
break;
case UIInterfaceOrientationPortraitUpsideDown:
self.window.frame = CGRectMake(0,0,w,h);
break;
case UIInterfaceOrientationLandscapeLeft:
self.window.frame = CGRectMake(statusHeight,0,w,h);
break;
case UIInterfaceOrientationLandscapeRight:
self.window.frame = CGRectMake(0,0,w,h);
break;
}
}
#end
2) Create category, and always use contentView instead of view
#interface UIViewController(iOS7_Fix)
#property (nonatomic, readonly) UIView* contentView;
- (void)updateViewIfIOS_7;
#end
#implementation UIViewController(iOS7_Fix)
static char defaultHashKey;
- (UIView *)contentView
{
return objc_getAssociatedObject(self, &defaultHashKey)?: self.view;
}
- (void)setContentView:(UIView *)val
{
objc_setAssociatedObject(self, &defaultHashKey, val, OBJC_ASSOCIATION_RETAIN_NONATOMIC) ;
}
- (void)updateViewIfIOS_7
{
if([[[UIDevice currentDevice] systemVersion] floatValue] < 7 || objc_getAssociatedObject(self, &defaultHashKey))
return;
UIView* exchangeView = [[UIView alloc] initWithFrame:self.view.bounds];
exchangeView.autoresizingMask = self.view.autoresizingMask;
exchangeView.backgroundColor = [UIColor blackColor];
UIView* view = self.view;
if(self.view.superview){
[view.superview addSubview:exchangeView];
[view removeFromSuperview];
}
[exchangeView addSubview:view];
self.view = exchangeView;
CGRect frame = self.view.bounds;
frame.origin.y += 20.0;
frame.size.height -= 20.0;
view.frame = frame;
view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self setContentView:view];
}
In every UIViewController:
- (void)viewDidLoad
{
[super viewDidLoad];
[self updateViewIfIOS_7];
UILabel* lab = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 130, 30)];
lab.backgroundColor = [UIColor yellowColor];
[self.contentView addSubview:lab];
//...
}
I am migrating my application to iOS 7. For handing the status bar issue I have added this code
if([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0f)
{
CGRect frame = self.navigationController.view.frame;
if(UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
{
frame.origin.y = 20;
}
else
{
frame.origin.x = 20;
}
[self.navigationController.view setFrame:frame];
}
This is working fine in normal case. If I am changing orientation (app supports only landscape orientation) or presenting any view controller and dismissing model view controller my view controller alignment changed. The status bar again overlaps my view controller. This piece of code is not working at all. Please guide me to fix this status bar issue.
Case 2: This is how I am presenting my view controller
ZBarReaderViewController *reader = [ZBarReaderViewController new];
reader.readerDelegate = self;
if(UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
reader.supportedOrientationsMask = ZBarOrientationMaskLandscape;
else
reader.supportedOrientationsMask = ZBarOrientationMaskPortrait;
[self presentModalViewController:reader animated:YES];
Ref:
Fix for status bar issue in IOS 7
Finally I fixed the status bar over lap issue using the delta value property in xcode5. First I have increased origin - y 20pxl to all the controller used in the Xib (it seams to be working fine only in IOS 7), after that I set the delta value for all the view controller origin -y to -20 it works fine in both iOS 6 and iOS 7.
Steps to do that.
Xcode 5 provide preview option to view the appearance of the xib in different view based on the OS version.
Choose preview option from assistant editor
Click assistant editor
and choose preview option to preview selected view controller in different version.
view controller view preview option.
in preview you can find the toggle option to preview view in different version. In preview u can feel the status bar issue clearly if its not fixed properly by toggle the version.
Three steps to fix the status bar issue:
step 1: Make sure the view target us 7.0 and later in File inspector.
Step 2 : Increase the origin - y with 20 pixel (exactly the size of the status bar) for all the controls added in the view controller.
Step 3 : Set the delta value of origin y to -20 for all the controls then only it will adjust automatically based on the version. Use preview now and feel the differ that the controls automatically adjust because of the delta value.
Once the status bar issue fixed, issue while presenting the model view (ZbarSDk controller) is also fixed automatically.
Preview screen :
I am late for this Answer, but i just want to share what i did, which is basically
the easiest solution
First of all-> Go to your info.plist File and add Status Bar Style->Transparent Black Style(Alpha of 0.5)
Now ,here it Goes:-
Add this code in your AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
//Whatever your code goes here
if(kDeviceiPad){
//adding status bar for IOS7 ipad
if (IS_IOS7) {
UIView *addStatusBar = [[UIView alloc] init];
addStatusBar.frame = CGRectMake(0, 0, 1024, 20);
addStatusBar.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:1]; //change this to match your navigation bar
[self.window.rootViewController.view addSubview:addStatusBar];
}
}
else{
//adding status bar for IOS7 iphone
if (IS_IOS7) {
UIView *addStatusBar = [[UIView alloc] init];
addStatusBar.frame = CGRectMake(0, 0, 320, 20);
addStatusBar.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:1]; //You can give your own color pattern
[self.window.rootViewController.view addSubview:addStatusBar];
}
return YES;
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
// Override point for customization after application launch.
self.viewController = [[[ViewController alloc] initWithNibName:#"ViewController" bundle:nil] autorelease];
self.window.rootViewController = self.viewController;
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7) {
[application setStatusBarStyle:UIStatusBarStyleLightContent];
[application setStatusBarHidden:NO withAnimation:UIStatusBarAnimationFade];
self.window.clipsToBounds =YES;
self.window.frame =CGRectMake(0,20,self.window.frame.size.width,self.window.frame.size.height-20);
}
[self.window makeKeyAndVisible];
return YES;
}
set the following to info.plist
View controller-based status bar appearance = NO;
To hide status bar in ios7 follow these simple steps :
In Xcode goto "Resources" folder and open "(app name)-Info.plist file".
check for "View controller based status bar appearance" key and set its value "NO"
check for "Status bar is initially hidden" key and set its value "YES"
If the keys are not there then you can add it by selecting "information property list" at top and click + icon
MUCH MUCH MUCH simpler answer:
Align the top of your view to the "top layout guide", but control-dragging "Top Layout Guide" to your view and setting the "vertical" constraint. See this answer for a picture reference.
The way it works is - the "Top Layout Guide" will automagically ajust itself for when the status bar is or is not there, and it will all work - no coding required!
P.S. In this particular example, the background showing through at the bottom should also be resolved by setting an appropriate vertical constraint of the view's bottom, to it's superview, or whatever...
Hear we can do this for all views at once
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Notification for the orientaiton change
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(applicationDidChangeStatusBarOrientation:)
name:UIApplicationDidChangeStatusBarOrientationNotification
object:nil];
// Window framing changes condition for iOS7 or greater
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7) {
statusBarBackgroundView = [[UIView alloc] initWithFrame:CGRectMake(0, -20, self.window.frame.size.width, 20)];//statusBarBackgroundView is normal uiview
statusBarBackgroundView.backgroundColor = [UIColor colorWithWhite:0.000 alpha:0.730];
[self.window addSubview:statusBarBackgroundView];
self.window.bounds = CGRectMake(0, -20, self.window.frame.size.width, self.window.frame.size.height);
}
// Window framing changes condition for iOS7 or greater
self.window.rootViewController = navigationController;
[self.window makeKeyAndVisible];
return YES;
}
And While we are using orientation we can add below method in app delegate to set it via orientation.
- (void)applicationDidChangeStatusBarOrientation:(NSNotification *)notification
{
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7) {
statusBarBackgroundView.hidden = YES;
UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
int width = [[UIScreen mainScreen] bounds].size.width;
int height = [[UIScreen mainScreen] bounds].size.height;
switch (orientation) {
case UIInterfaceOrientationLandscapeLeft:
self.window.bounds = CGRectMake(-20,0,width,height);
statusBarBackgroundView.frame = CGRectMake(-20, 0, 20, height);
break;
case UIInterfaceOrientationLandscapeRight:
self.window.bounds = CGRectMake(20,0,width,height);
statusBarBackgroundView.frame = CGRectMake(320, 0, 20, height);
break;
case UIInterfaceOrientationPortraitUpsideDown:
statusBarBackgroundView.frame = CGRectMake(0, 568, width, 20);
self.window.bounds = CGRectMake(0, 20, width, height);
break;
default:
statusBarBackgroundView.frame = CGRectMake(0, -20, width, 20);
self.window.bounds = CGRectMake(0, -20, width, height);
break;
}
statusBarBackgroundView.hidden = NO;
}
}
You should Add below navigation controller category for it
.h
#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>
#interface UINavigationController (iOS6fix)
#end
.m
#import "UINavigationController+iOS6fix.h"
#implementation UINavigationController (iOS6fix)
-(BOOL)shouldAutorotate
{
return [[self.viewControllers lastObject] shouldAutorotate];
}
-(NSUInteger)supportedInterfaceOrientations
{
return [[self.viewControllers lastObject] supportedInterfaceOrientations];
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return [[self.viewControllers lastObject] preferredInterfaceOrientationForPresentation];
}
#end
With Salesforce SDK 2.1 (Cordova 2.3.0) we had to do the following to get the status bar appear on the initial load of the App and coming back from the background (iPhone and iPad):
Contrarily to other solutions posted here, this one seems to survive rotation of the device.
1-Create a category of theSFHybridViewController
#import "SFHybridViewController+Amalto.h"
#implementation SFHybridViewController (Amalto)
- (void)viewWillAppear:(BOOL)animated
{
//Lower screen 20px on ios 7
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7) {
CGRect viewBounds = self.view.bounds;
viewBounds.origin.y = 20;
viewBounds.size.height = viewBounds.size.height - 20;
self.webView.frame = viewBounds;
}
[super viewWillAppear:animated];
}
- (void)viewDidLoad
{
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7) {
CGRect viewBounds = self.view.bounds;
viewBounds.origin.y = 20;
viewBounds.size.height = viewBounds.size.height - 20;
self.webView.frame = viewBounds;
}
[super viewDidLoad];
}
#end
2-Add to AppDelegate.m imports
#import "SFHybridViewController+Amalto.h"
3-Inject at the end of of method didFinishLaunchingWithOptions of AppDelegate
//Make the status bar appear
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7) {
[application setStatusBarStyle:UIStatusBarStyleLightContent];
[application setStatusBarHidden:NO withAnimation:UIStatusBarAnimationFade];
}
4-Add to App-Info.plist the property
View controller-based status bar appearance with value NO
i solved this by using below code
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
if(landScape mode)
if ([UIDevice currentDevice].systemVersion.floatValue>=7) {
CGRect frame = self.window.frame;
frame.size.width -= 20.0f;
frame.origin.x+= 20.0f;
self.window.frame = frame;
}
if(portrait)
if ([[[UIDevice currentDevice]systemVersion]floatValue] >= 7.0) {
[application setStatusBarStyle:UIStatusBarStyleLightContent];
CGRect frame = self.window.frame;
frame.origin.y += 20.0f;
frame.size.height -= 20.0f;
self.window.frame = frame;
}
return YES;
}
#define _kisiOS7 ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0)
if (_kisiOS7)
{
[[UINavigationBar appearance] setBarTintColor:_kColorFromHEX(#"#011C47")];
}
else
{
[[UINavigationBar appearance] setBackgroundColor:_kColorFromHEX(#"#011C47")];
[[UINavigationBar appearance] setTintColor:_kColorFromHEX(#"#011C47")];
}
There are several different ways. One approach is to use .plist file
Add a new key "View controller-based status bar appearance" and set value as "NO".
Add another key "Status bar is initially hidden" and set value as "YES".
This will hide status bar throughout project.
just set the following code in viewWillAppear.
if ([[[UIDevice currentDevice] systemVersion] floatValue]<= 7) {
self.edgesForExtendedLayout = UIRectEdgeNone;
}
I am having troubles after updating to Xcode 4.2.
Before I used the following codes for making custom navigation bar, but when i use iPhone 5.0 simulator, it fails whereas in iPhone 4.2 simulator it was ok.
May I know what is the problem and how can i fix this?
Many thanks
#implementation UINavigationBar (UINavigationBarCustomDraw)
- (void) drawRect:(CGRect)rect {
[self setTintColor:[UIColor colorWithRed:0.4f
green: 0.0f
blue:0.4f
alpha:1]];
if ([self.topItem.title length] > 0 && ![self.topItem.title isEqualToString:#""])
{
[[UIImage imageNamed:#"purple.jpg"] drawInRect:rect];
}
}
#end
If you need custom UINavigationbar with some image so you need to put this code in rootViewController that is first view of navigate stack (A > B > C , so you have to put this in A)
- (void)viewDidLoad
{
[super viewDidLoad];
[[UIDevice currentDevice] systemVersion];
if ([[[UIDevice currentDevice] systemVersion] floatValue] > 4.9) {
//iOS 5
UIImage *toolBarIMG = [UIImage imageNamed: #"purple.jpg"];
if ([self.navigationController.navigationBar respondsToSelector:#selector(setBackgroundImage:forBarMetrics:)]) {
[[UINavigationBar appearance] setBackgroundImage:toolBarIMG forBarMetrics:UIBarMetricsDefault];
}
} else {
//iOS 4
[self.navigationController.navigationBar insertSubview:[[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"purple.jpg"]] autorelease] atIndex:0];
}
}
}