Work with iOS 7 top layout guide - ios

I'm designing a view controller in my storyboard. It contains a navigation bar at the top and a content view. I've set the constraints as
Navigation bar's top space to top layout guide = 0
Content view fills the remaining space (leading and trailing space to superview = 0 ; bottom space to superview = 0, top space to navigation bar =0)
Because I want to display the status bar, I'm using the top layout guide to make the navigation bar displayed just under it. It works with a standard UINavigationBar.
But I have custom navigation bar : I've created my own class inheriting UINavigationBar.
The problem is : when specifying this custom class for the navigation bar in the Identity Inspector of the interface builder, the bar is not at the right place when running the app.
Is there anything to add or modify in initWitFrame or initWithCoder, or something else to do, so my custom navigation bar will follow the constraints of my storyboard.
EDIT - source added
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// additional setup
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:(NSCoder *)aDecoder];
if (self) {
// same setup than initWithFrame:
}
return self;
}
- (void)drawRect:(CGRect)rect
{
//[self setFrame:rect]; navigation bar is not at the right place if uncommented
}

Why are you calling [self setFrame:rect];? frame is correctly set up at this point, you shouldn't change it. Also, rect is in an internal coordinate system of the navigation bar.

Related

Centering a custom navigation bar item using layout anchors

I have created a custom navigation item (with both title and a UISearchBar) to replace the standard navigation title on iOS and I am trying to center it in a UINavigationBar using layout anchors.
However, the layout constraint [self.navigationItem.titleView.centerXAnchor constraintEqualToAnchor:self.navigationController.navigationBar.centerXAnchor].active = YES; does not work and the UIView containing the search bar shows up on the left side of the UINavigationBar.
Other constraints:
The width should be 1/3 of the screen width
I want to avoid setting width as a constant to make the view responsive in response to orientation and other layout changes
How to center the navigation bar item?
ScreenViewController.m:
- (void) viewDidLoad {
[super viewDidLoad];
// Create the search bar
self.searchBar = [SearchBar new];
// Create a new UIView as titleView (otherwise I'm receiving an error)
self.navigationItem.titleView = [UIView new];
// Add into view hierarchy and turn off auto constraints
[self.navigationController.navigationBar addSubview:self.navigationItem.titleView];
[self.navigationItem.titleView setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.navigationItem.titleView addSubview:self.searchBar.view];
[self.searchBar.view setTranslatesAutoresizingMaskIntoConstraints:NO];
// Set anchors for the wrapping view
[self.navigationItem.titleView.widthAnchor constraintEqualToAnchor:self.navigationController.navigationBar.widthAnchor multiplier:1.0/3.0].active = YES;
//[self.navigationItem.titleView.heightAnchor constraintEqualToConstant:50.0].active = YES;
[self.navigationItem.titleView.topAnchor constraintEqualToAnchor:self.navigationController.navigationBar.topAnchor].active = YES;
[self.navigationItem.titleView.centerXAnchor constraintEqualToAnchor:self.navigationController.navigationBar.centerXAnchor].active = YES;
[self.navigationItem.titleView.bottomAnchor constraintEqualToAnchor:self.navigationController.navigationBar.bottomAnchor].active = YES;
// Set anchors for the search bar
[self.searchBar.view.widthAnchor constraintEqualToAnchor:self.navigationItem.titleView.widthAnchor multiplier:1.0].active = YES;
//[self.navigationItem.titleView.heightAnchor constraintEqualToConstant:50.0].active = YES;
[self.searchBar.view.topAnchor constraintEqualToAnchor:self.navigationItem.titleView.topAnchor].active = YES;
[self.searchBar.view.bottomAnchor constraintEqualToAnchor:self.navigationItem.titleView.bottomAnchor].active = YES;
//[self.searchBar.view.leftAnchor constraintEqualToAnchor:self.navigationItem.titleView.leftAnchor].active = YES;
//[self.searchBar.view.rightAnchor constraintEqualToAnchor:self.navigationItem.titleView.rightAnchor].active = YES;
[self.searchBar.view.centerXAnchor constraintEqualToAnchor:self.navigationItem.titleView.centerXAnchor].active = YES;
}
Expected result:
What I get (the title view is not centered):
View hierarchy:
What you're doing is both impossible and illegal. You are not making a "custom navigation bar item" at all (as claimed in your title). You are attempting to add a subview directly to a navigation bar. You can't do that. The way to put something into a navigation bar is to assign it into a view controller's navigationItem, either as a bar button item or as the navigation item's real titleView. (You call your variable titleView, but you are not in fact making it the navigation item's titleView.)
Here is my solution, changing only the title view, setting its width anchor to a fraction of the navigation bar's to make it responsive:
// Create search bar and set it as title view
self.searchBar = [SearchBar new];
self.navigationItem.titleView = self.searchBar.view;
// Turn auto constraints off
[self.searchBar.view setTranslatesAutoresizingMaskIntoConstraints:NO];
// Set the layout anchors
[self.navigationItem.titleView.widthAnchor constraintEqualToConstant:250.0].active = YES; // Had to set the width to a constant
// Was still causing problems
//[self.navigationItem.titleView.widthAnchor constraintEqualToAnchor:self.navigationController.navigationBar.widthAnchor multiplier:1.0/3.0].active = YES;// Still need to access the navigation bar's anchor
[self.navigationItem.titleView.heightAnchor constraintEqualToConstant:50.0].active = YES;

Strange spaces at the top & bottom of ICViewPager

I make use of ICViewPager to create tabs of contents. However, the layout looks weird as there are strange spaces at the top & bottom of ICViewPager's content view.
As you can see below, I have a UINavigationBar at the top of the screen, which is generated by the embedding UINavigationController. Then, the UINavigationController is made to be one of the tabs in a UITabbar Controller. Here is the structure:
UITabbarController --> UINavigationController --> TabVC (which contains ICViewPager) --> Content views: Content1VC, Content2VC, Content3VC
Here are the codes in TabVC (which configs to have <ViewPagerDataSource, ViewPagerDelegate>):
// in viewDidLoad
self.dataSource = self;
self.delegate = self;
self.edgesForExtendedLayout = UIRectEdgeNone;
and for the delegate methods:
#pragma mark - ViewPagerDataSource
- (NSUInteger)numberOfTabsForViewPager:(ViewPagerController *)viewPager {
return tabsContents.count;
}
- (UIView *)viewPager:(ViewPagerController *)viewPager viewForTabAtIndex:(NSUInteger)index {
UILabel *label = [UILabel new];
label.text = [tabsContents objectAtIndex:index];
label.textColor = [UIColor colorWithRed:136/255.0 green:136/255.0 blue:136/255.0 alpha:1.0f];
[label sizeToFit];
return label;
}
- (UIViewController *)viewPager:(ViewPagerController *)viewPager contentViewControllerForTabAtIndex:(NSUInteger)index {
UIViewController *vc = [self.storyboard instantiateViewControllerWithIdentifier:[tabsVC objectAtIndex:index]];
return vc;
}
The functions look okay, but the layout does not span through the whole spaces as it expects to do so. The red spaces (I made the TabVC view's background color to red to illustrate the issue) are not expected to appear. How do I make the ICViewPager occupy the red spaces?
Note: This appears only after the view is popped back from a pushed view controller, or changing tabs in UITabbarController
I think it is a conflict between automaticallyAdjustsScrollViewInsets and edgesForExtendedLayout.
From this answer :
edgesForExtendedLayout
Basically, with this property you set which sides of your view can be extended to cover the whole screen. Imagine that you push a UIViewController into a UINavigationController, when the view of that view controller is laid out, it will start where the navigation bar ends, but this property will set which sides of the view (top, left, bottom, right) can be extended to fill the whole screen.
and
automaticallyAdjustsScrollViewInsets
This property is used when your view is a UIScrollView or similar, like a UITableView. You want your table to start where the navigation bar ends, because you wont see the whole content if not, but at the same time you want your table to cover the whole screen when scrolling. In that case, setting edgesForExtendedLayout to None won't work because your table will start scrolling where the navigation bar ends and it wont go behind it.
So, automaticallyAdjustsScrollViewInsets defaults to YES and thus inserts a positive inset at the top equal to the height of the nav bar. now when you apply self.edgesForExtendedLayout = UIRectEdgeNone, that inset creeps out from under the nav bar causing said issue.

iOS Autolayout how to stretch out subview in superview

As you can see on the screenshot here is gray UIView which I use as a container. In the code I allocate a UINavigationController and add it's view as a subview to the gray container view. The UINavigationController has RootViewController (you can see the cut off table view on the left side - it is my root view controller of UINavigationController).
My question is how to pin all sides to the superview bounds. Because right now I just can see a part of the RootViewController view.
In the storyboard I have set all constraints as for gray container view as for RootViewController view of UINavigationController. If I normally PushViewController instead of adding as a subview it shows without cut off issue.
Here is a code I have in UIViewController that contains gray container view:
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self)
{
KNCouponsViewController *couponsViewControllerByList = [KNInstantiateHelper instantiateViewControllerWithIdentifier:#"KNCouponsViewController"];
self.theNavigationController = [[UINavigationController alloc] initWithRootViewController:couponsViewControllerByList];
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self.theContainerView addSubview:self.theNavigationController.view];
}
Here is constraints for the gray container view
Here is constraints for the view of root view controller which has cut off table:
Seems like a simple solution is to set UINavigationController view the same width and hight as the gray container has:
- (void)viewDidLoad
{
[super viewDidLoad];
CGRect rect = self.theContainerView.frame;
rect.origin.x = 0;
rect.origin.y = 0;
[self.theNavigationController.view setFrame:rect];
[self.theContainerView addSubview:self.theNavigationController.view];
}
Then it works correct. But I think if we will not modify a frame only then it will work?
there are two ways.
1 way is to call addSubView code in viewDidLayoutSubview method
2nd way is to use following code for settings frame
[self.theContainerView addSubview:self.theNavigationController.view];
[self.theNavigationController.view setFrame: self.theContainerView.frame];
Note: If you use first way then add your code in
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self.theContainerView addSubview:self.theNavigationController.view];
[self.containerView layoutIfNeeded];
});

Strange UISearchDisplayController view offset behavior in iOS 7 when embedded in navigation bar

I am building an iOS 7-only app. I am trying to set a UISearchDisplayController into the navigation bar.
I have it set up like this: In the storyboard, I added a "Search Bar and Search Display Controller" to my view controller's view, and set it at (0,0) relative to the top layout guide. I set constraints to pin to left, top and right. (I played with the constraints, i removed them completely, it doesn't matter) On top of that I have my Table view. When I added the search bar to the view in the storyboard, it automatically setup outlets for searchDisplayController and searchBar delegate. In code I have self.searchDisplayController.displaysSearchBarInNavigationBar = YES; I have two problems:
1) Without any buttons showing for the search bar (Interface builder -> select search bar -> Options: none selected) the search bar is in the middle of the screen:
If I click on the navigation bar, it starts editing the search bar:
notice also that the dark overlay appears to be offset from the navigation bar. It seems to me that the space is the same height as the navigation bar. Like it has been shifted down by that much. Also, when it displays the search results, the top of the content view is shifted down by the same amount (more pictures follow), which brings me to the second problem.
2) I messed around with it for a while and decided to check the option to have it show the cancel button. Now I have the search bar embedded in the nav bar correctly, but the overlay is still shifted down:
Again, when the search results table view appears, it is shifted down by the same amount (notice the scroll bar on the right side):
Even more bizarrely, I set a border on the search display controller's tableview layer, and it appears correct:
I have never used the UISearchDisplayController before and I unfamiliar with how to set it up, but functionally it works fine. I have read some other similar posts but the only advice is to hack it up by adjusting frames and setting manual offsets. I'd prefer to know what is causing this, is it a bug? Something I'm doing wrong? If it's a bug I can wait for a fix. It seems like such a basic thing that a thousand people must have done without any problem so I feel like I'm not setting it up correctly somehow. Thanks for you input.
I remember running into the same exact problem that you are observing.There could be a couple of solutions you can try.
If you are using storyboards
You should click on the view controller or TableView Controller which you have set up for your tableview and go to its attribute inspector and look under ViewController section and set the Extend Edges section to be under Top Bars.
If you are not using storyboards you can manually set the settings using the viewcontrollers edgesForExtendedLayout property and that should do the trick. I was using storyboards.
In my case, using storyboards, I had to check both Under Top Bars and Under Opaque Bars and leave Under Bottom Bars unchecked.
In my case, I actually had to uncheck all the Extended Edges boxes (essentially the same as programmatically setting Extended Edges to UIRectEdgeNone I believe) in my Storyboard in order to stop my search bar from offsetting itself. Thank you guys!
definesPresentationContext = true
override func viewDidLoad() {
super.viewDidLoad()
searchController = UISearchController(searchResultsController: nil)
searchController.searchResultsUpdater = self
searchController.hidesNavigationBarDuringPresentation = false
searchController.dimsBackgroundDuringPresentation = true
searchController.searchBar.searchBarStyle = UISearchBarStyle.Prominent
self.tableView.tableHeaderView = searchController.searchBar
definesPresentationContext = true
or see UISearchBar presented by UISearchController in table header view animates too far when active
My problem was just Adjust scroll view inserts. After change to false I didn't have problem
I had a same problem. And I solve this issue with adding view object under the tableview.
Add new ViewController on the Storyboard
Drag TableView to the new VC
Drag Table Cell to the TableView
Make a Connection for TableView DataSource, TableView Delegate to the new VC
I had very similar behavior happening. For me, the solution was to uncheck Extend Edges Under Top Bar in the storyboard settings for the parent view controller (I've turned off transparent navbars, not sure if that effects anything). If you're not using storyboard, you have to set [UIViewController edgesForExtendedLayout].
From the Apple docs:
This property is only applied to view controllers that are embedded in containers, such as UINavigationController or UITabBarController. View controllers set as the root view controller do not react to this property. Default value is UIRectEdgeAll.
Unfortunately none of the above solutions worked for me, I'm using a UITableViewController.
This link helped:
http://petersteinberger.com/blog/2013/fixing-uisearchdisplaycontroller-on-ios-7/
I put the code below for convenience:
static UIView *PSPDFViewWithSuffix(UIView *view, NSString *classNameSuffix) {
if (!view || classNameSuffix.length == 0) return nil;
UIView *theView = nil;
for (__unsafe_unretained UIView *subview in view.subviews) {
if ([NSStringFromClass(subview.class) hasSuffix:classNameSuffix]) {
return subview;
}else {
if ((theView = PSPDFViewWithSuffix(subview, classNameSuffix))) break;
}
}
return theView;
}
- (void)correctSearchDisplayFrames {
// Update search bar frame.
CGRect superviewFrame = self.searchDisplayController.searchBar.superview.frame;
superviewFrame.origin.y = 0.f;
self.searchDisplayController.searchBar.superview.frame = superviewFrame;
// Strech dimming view.
UIView *dimmingView = PSPDFViewWithSuffix(self.view, #"DimmingView");
if (dimmingView) {
CGRect dimmingFrame = dimmingView.superview.frame;
dimmingFrame.origin.y = self.searchDisplayController.searchBar.frame.size.height;
dimmingFrame.size.height = self.view.frame.size.height - dimmingFrame.origin.y;
dimmingView.superview.frame = dimmingFrame;
}
}
- (void)setAllViewsExceptSearchHidden:(BOOL)hidden animated:(BOOL)animated {
[UIView animateWithDuration:animated ? 0.25f : 0.f animations:^{
for (UIView *view in self.tableView.subviews) {
if (view != self.searchDisplayController.searchResultsTableView &&
view != self.searchDisplayController.searchBar) {
view.alpha = hidden ? 0.f : 1.f;
}
}
}];
}
// This fixes UISearchBarController on iOS 7. rdar://14800556
- (void)correctFramesForSearchDisplayControllerBeginSearch:(BOOL)beginSearch {
if (PSPDFIsUIKitFlatMode()) {
[self.navigationController setNavigationBarHidden:beginSearch animated:YES];
dispatch_async(dispatch_get_main_queue(), ^{
[self correctSearchDisplayFrames];
});
[self setAllViewsExceptSearchHidden:beginSearch animated:YES];
[UIView animateWithDuration:0.25f animations:^{
self.searchDisplayController.searchResultsTableView.alpha = beginSearch ? 1.f : 0.f;
}];
}
}
- (void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller {
[self correctFramesForSearchDisplayControllerBeginSearch:YES];
}
- (void)searchDisplayControllerDidBeginSearch:(UISearchDisplayController *)controller {
[self correctSearchDisplayFrames];
}
- (void)searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller {
[self correctFramesForSearchDisplayControllerBeginSearch:NO];
}
- (void)searchDisplayController:(UISearchDisplayController *)controller didShowSearchResultsTableView:(UITableView *)tableView {
// HACK: iOS 7 requires a cruel workaround to show the search table view.
if (PSPDFIsUIKitFlatMode()) {
controller.searchResultsTableView.contentInset = UIEdgeInsetsMake(self.searchDisplayController.searchBar.frame.size.height, 0.f, 0.f, 0.f);
}
}
Go to storyboard.
Click on the view controller.
Go to attribute inspector under the ViewController section.
Set the Extend Edges section to be Under Top Bars and Under Opaque Bars.
Make sure to un-check Under Bottom Bars.

Switching UIViews without changing the top bar

I'm new to ios programming and I can't figure this out. I have a view-based application with a navigation bar and a "custom" bar under the navigation bar (An image with two UIButtons on it). Under this two bars I have two UIViews that must be shown at the press of the corresponding button on the "custom" bar, but the top bars must remain the same the whole time.
Do you have any ideas how to do this?
You can simply add both the views to your viewControllers subview and use the hidden property of the views to show/hide the correct view.
-(void) viewDidLoad
{
[super viewDidLoad];
[self.view addSubview:view1];
[self.view addSubview:view2];
view1.hidden=YES;
view2.hidden=YES;
}
-(IBAction) btn1Pressed
{
view1.hidden=NO;
view2.hidden=YES;
}
-(IBAction) btn2Pressed
{
view1.hidden=YES;
view2.hidden=NO;
}

Resources