Good articles to my personal technology blog: https://cainluo.github.io/15101116434794.html


With the release of more and more sizes of apple dad’s devices, there are also changes to the design of iOS, especially after iOS 11, the navigation bar is bigger than large, then can change the size when scrolling and so on.

But none of that was a problem, and just like WWDC 2017, Apple dad was showing us something that he wanted us to use for a long time: auto layout.

With the release of 10.5 inch, 5.8 inch and 12.9 inch devices, it has become more and more difficult for us developers to adapt to multiple sizes. However, with the release of automatic layout, we developers can pay more attention to the business development of App, and no longer need to calculate the difference between this and that. What if I rotate it.

Let’s just use a simple little project here.

Reprint statement: if you need to reprint this article, please contact the author, and indicate the source, and can not modify this article without authorization.

Bigger headlines

In iOS 11, one of the most obvious changes is the addition of a headline in the navigation bar:

Here we can set a BOOL property of the UINavigationBar to determine whether to display the header:

@property (nonatomic.readwrite.assign) BOOL prefersLargeTitles;
Copy the code

So if you’re in a Storyboard, you can also set this property in the UINavigationBar on the UINavigationController, check it to show the title, leave it blank.

Although prefersLargeTitles is the main switch to open titles, within each controller we can use the UINavigationItem on each controller to indicate whether to display titles or normal titles, and there are three types of titles:

typedef NS_ENUM(NSInteger.UINavigationItemLargeTitleDisplayMode) {
    UINavigationItemLargeTitleDisplayModeAutomatic.UINavigationItemLargeTitleDisplayModeAlways.UINavigationItemLargeTitleDisplayModeNever,}NS_SWIFT_NAME(UINavigationItem.LargeTitleDisplayMode);
Copy the code

To use this property, prefersLargeTitles has to be set to YES to play with each of the three display modes, which is Automatic by default. This allows us to set whether or not the controller needs to display titles

If you don’t want to write a Method Swizzling for each controller, you can write a Method Swizzling in RunTime, or encapsulate a RootController and a ChildController.

Search controller

The second change is the integration of UISearchController into UINavigationController. UISearchController is nothing new, but after iOS 11, We can set the searchController property of the UINavigationItem to UISearchController.

    UISearchController *searchController = [[UISearchController alloc] initWithSearchResultsController:nil];
    
    self.navigationItem.searchController = searchController;
    self.navigationItem.hidesSearchBarWhenScrolling = YES;
Copy the code

There is also a property called hidesSearchBarWhenScrolling, if set to YES, then at the time of operation, can according to slide to hide the searchController, if set to NO, has been showing, this attribute is the default is YES.

The safety area

When iOS 11 was released, a lot of people had a black question mark about what Safe areas were.

In fact, back in iOS 7 there were two properties, topLayoutGuide and bottomLayoutGuide, that were used for automatic layout, and since iOS 7 introduced the concept of translucence, if we were to use a layout control like UITableView, The content is displayed behind the UINavigationBar and UITabBar.

So if we set up topLayoutGuide and bottomLayoutGuide, we won’t have this problem, but unfortunately, those two properties belong to the Controller and not the UIView, so to solve this problem, apple dad did a new thing, It’s the current safe zone, a thing called safearea Way outGuide, which belongs to UIVIew.

    @property(nonatomic.readonly.strong) UILayoutGuide *safeAreaLayoutGuide;
Copy the code

SafeAreaLayoutGuide is a great way for us to create constraints within security zones, so we can easily adapt to a wide variety of devices, such as the iPhone X, which emphasizes security zones.

If we just want to measure the safe zone, the safeAreaInsets will return some value.

typedef struct UIEdgeInsets {
    CGFloat top, left, bottom, right;
} UIEdgeInsets;
Copy the code

If we want to know when these values change, we can use two apis provided by the system:

/ / UIView API
- (void)safeAreaInsetsDidChange;

// UIViewController
- (void)viewSafeAreaInsetsDidChange;
Copy the code

If we do not modify the safeAreaInsets, the value we get from safeAreaInsets is 0. If we modify the safeAreaInsets manually, we can set additionalSafeAreaInsets:

    self.additionalSafeAreaInsets = UIEdgeInsetsMake(100.50.0.0);
Copy the code

Then look at the effect:

If you’re using a storyboard, you can click on any control or controller and check to see if the Use Safe Area Layout Guides are checked. If so, Xcode then automatically converts the layout constraints that were previously at the top and bottom into the safe zone.

Uncheck the Use of Safe Area Layout Guides and then manually set the Guides to SuperView:

Note: If we had set the constraint on topLayoutGuide and bottomLayoutGuide, we would have checked off the Use Safe Area Layout Guides, UINavigationBar behind and UITabBar will display content, if we want to limit the view to the top, we should be in the view of topAnchor and topLayoutGuide bottomAnchor add a constraint, but in 11 of the iOS, We can be in topAnchor and safeAreaLayoutGuide. Add a constraint between topAnchor oh. PS: topLayoutGuide indicates the area covered by the status bar and navigation bar safeAreaLayoutGuide indicates the area not covered by the status bar and navigation bar

margin

The Margins feature some new changes as well, including some properties that are deprecated:

Be replaced:

@property (nonatomic) UIEdgeInsets layoutMargins;
Copy the code

Properties of substitutes:

@property (nonatomic) NSDirectionalEdgeInsets directionalLayoutMargins;
Copy the code

The new directionalLayoutMargins feature changes to the reading direction and uses leading and trailing instead of left and right, although the layoutMarginsGuide is still the same

When we set directionalLayoutMargins, its value will be added to the systemMinimumLayoutMargins, used to determine the actual margin of view, if we don’t want a system of minimum margin, We can set the viewRespectsSystemMinimumLayoutMargins to NO is ok.

The last attribute added is:

    @property (nonatomic) BOOL insetsLayoutMarginsFromSafeArea;
Copy the code

If this property is set to YES, the view margin we need to lay out is relative to the security zone. If set to NO, the view margin we need to lay out is relative to the other view margins. The default is YES.

Scroll views

Before iOS 11, if we wanted to use automatic layout for UIScrollView, we would have to write some logic to determine whether to constrain the scrolling view of UIScrollView or its content area. Sometimes constraint errors would occur when adding constraints. Let’s say you’re using a Storyboard layout.

But in iOS 11, apple’s dad added two constraint attributes to UIScrollView to solve this problem:

@property(nonatomic.readonly.strong) UILayoutGuide *contentLayoutGuide;
@property(nonatomic.readonly.strong) UILayoutGuide *frameLayoutGuide;
Copy the code

These two properties allow us to be more precise when adding constraints to UIScrollView, but they are not good news for Storyboard developers because they can only be used in code.

In addition to the two properties above, there is another thing that affects the UIScrollView content area:

@property(nonatomic.assign) BOOL automaticallyAdjustsScrollViewInsets;
Copy the code

This property defaults to YES, and sometimes our view doesn’t show up at the bottom of the UINavigationBar because of this property, so just set it to NO.

Thankfully, in iOS 11 this property was disabled and the system no longer automatically sets the contents of UIScrollView. Now UIScrollView content insertion adjustments are calculated from the security zone and the values we set in contentInset, controlled by the following properties, Temporarily, there are four types of control:

@property(nonatomic) UIScrollViewContentInsetAdjustmentBehavior contentInsetAdjustmentBehavior;

typedef NS_ENUM(NSInteger.UIScrollViewContentInsetAdjustmentBehavior) {
    UIScrollViewContentInsetAdjustmentAutomatic.UIScrollViewContentInsetAdjustmentScrollableAxes.UIScrollViewContentInsetAdjustmentNever.UIScrollViewContentInsetAdjustmentAlways,
} API_AVAILABLE(ios(11.0),tvos(11.0));
Copy the code

Demo

To make things easier to understand, here’s a Demo that uses Storyboard plus code.

ScrollView layout:

UIImageView layout:

TipsView layout:

PS: If your constraint doesn’t feel right, just double-click on it and go inside and change it.

The code here is not difficult, mainly for the layout of TipsView and ScrollView:

    CGFloat scrollIndicatorMargin = 8;
    
    self.tipsView.layer.cornerRadius = 8;
    
    [self.tipsView.leadingAnchor constraintEqualToAnchor:self.scrollView.frameLayoutGuide.leadingAnchor
                                                constant:scrollIndicatorMargin].active = YES;
    
    [self.tipsView.trailingAnchor constraintEqualToAnchor:self.scrollView.frameLayoutGuide.trailingAnchor
                                                constant:-scrollIndicatorMargin].active = YES;
    
    [self.tipsView.bottomAnchor constraintEqualToAnchor:self.scrollView.frameLayoutGuide.bottomAnchor
                                                constant:-scrollIndicatorMargin].active = YES;
    
    self.additionalSafeAreaInsets = UIEdgeInsetsMake(0.0.self.tipsView.frame.size.height + scrollIndicatorMargin,
                                                     scrollIndicatorMargin);
    
    self.scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
Copy the code

If you want to see the constraints in more detail, you can open Demo to explore.

Adaptive Cells

When iOS 7 came out, UITableView had a property called:

    @property (nonatomic) CGFloat estimatedRowHeight;
Copy the code

We can set to UITableViewAutomaticDimension, give Cell constraints of the internal view of adaptation, so the Cell can be adaptive.

But these are all need our own manual to adaptation, in iOS, this thing is no longer need us to write, the default is UITableViewAutomaticDimension.

This property is useful not only for ordinary cells, but also for SectionHeader and SectionFooter. If you don’t need it, you can manually set estimatedRowHeight to 0.

If you’re working with older Xcode and you’re doing it in Storyboard, you can go to the Storyboard and go to the UITableView and go to the property and say Automatic:

But using auto layout on a large scale can create a performance problem, as discussed earlier, so how do you do it? We can set estimatedRowHeight to roughly how high we want to display the data, and then determine all the constraints in the Cell, and then the performance of the UITableView can be improved. Of course, the best way to do this is to use asynchronous loading, or high caching, or something like that. You can go to Baidu search.

Refresh the controller

If we add UIRefreshControl to the corresponding UITableViewController, it will be automatically loaded into the UINavigationBar:

The code is also very simple:

    self.tableView.refreshControl = [[UIRefreshControl alloc] init];
    
    [self.tableView.refreshControl addTarget:self
                                      action:@selector(refreshControllerAction)
                            forControlEvents:UIControlEventValueChanged];
Copy the code
- (void)refreshControllerAction {
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        
        [self.tableView.refreshControl endRefreshing];
    });
}
Copy the code

UITableVIew splitter line

We all know that in iOS 7, Apple added some offsets to the Separators of the UITableView, but back then you could only set them through separatorInset, which was too rigid and inflexible.

After iOS 11, Apple dad added another attribute:

@property (nonatomic) UITableViewSeparatorInsetReference separatorInsetReference;

typedef NS_ENUM(NSInteger.UITableViewSeparatorInsetReference) {
    UITableViewSeparatorInsetFromCellEdges.UITableViewSeparatorInsetFromAutomaticInsets
} API_AVAILABLE(ios(11.0), tvos(11.0));
Copy the code
  • UITableViewSeparatorInsetFromCellEdges:The default, if this property is used, is the starting coordinate of the dividing lineCellThe edge value of delta, which is 0.
  • UITableViewSeparatorInsetFromAutomaticInsets: if using this property, the line will start coordinates with default values, such as on the left side of the offset is 15.

Stack Views

We all know that UIStackViews, a flexible layout stack view, was released in iOS 9 so we don’t have to manage a lot of constraints.

But there are some scene did not adapt to, you can now solve the problem, in iOS, apple’s dad added more characteristics to it, such as in multiple views, we have a view is more special, to leave more far, before is impossible, can now, let’s see:

The detailed constraint layout is troublesome to go to the project to have a look, here is not to say, directly look at the code:

    [UIViewPropertyAnimator runningPropertyAnimatorWithDuration:0.5
                                                          delay:0
                                                        options:UIViewAnimationOptionCurveEaseInOut
                                                     animations:^{
        
                                                         if (self.action) {
                                                             
                                                             [self.stackView setCustomSpacing:self.customSpacing
                                                                                    afterView:self.sunImageView];
                                                         } else{[self.stackView setCustomSpacing:0
                                                                                    afterView:self.sunImageView];
                                                         }
                                                         
                                                         self.action = !self.action;
                                                         
                                                     } completion:nil];
Copy the code

The vector diagram

In previous versions of Xcode, if you wanted to use a vector image, Xcode and iOS would automatically generate a different size image for the vector image at compile time.

In Xcode 9, we can check something that tells the system to keep vector data:

That way, when we or other users turn on a large font in accessibility, we can set one of the UIImageView properties to make sure it works:

@property (nonatomic) BOOL adjustsImageSizeForAccessibilityContentSizeCategory;
Copy the code

In the Demo, I only set the properties of one icon, so this icon is displayed normally and the other two are not:

This property can be found in the Storyboard, or it can be implemented in code, as long as it’s a UIImageView.

Custom navigation bar view

Finally, add a custom view to UINavigationBar or UIToolbar.

Let’s go back to the sun, the stars, and the moon, and just have a UIImageView and a UILabel.

This layout is similar to customizing the UITableViewCell with constraints, and is extremely simple:

When we run it, we can see that it’s normal, but there’s a white background color, so we just need to find the corresponding UIView and clean it up.

conclusion

There are a number of changes that will affect the automatic layout in iOS 11, as well as some new features that will make it easier for us developers to develop. If reading the article isn’t enough for you, check out the original WWDC 2017 introduction:

  • WWDC17 Session 204 – Updating Your App for iOS 11 apple.co/2syu3Tt

engineering

https://github.com/CainRun/iOS-11-Characteristic/tree/master/3.Layout

The last