Xcode 6 AutoLayout Size Classes, xcodeautolayout
1. Basic Concepts
Before the appearance of iPad and iPhone 5, iOS devices only have one size. We only need to consider the device direction when making screen adaptation. Many applications do not support redirection. In this way, there will be no screen adaptation work. With the launch of iPad, iPhone 5, and the next iPhone 6, the screen size has become an object to consider. Before iOS7, when creating a UI for an application, especially for the universal application, we always first think about how long and wide the target device is, and how the layout should be changed after the direction is changed, then layout. IOS6 introduces AutoLayout to help developers use constraints for layout, so that in some cases we do not need to consider the size, but can focus on using constraints to specify the location.
Since we have AutoLayout, there is no problem in specifying the position and size of the View through constraints. In this regard, the specific size and direction of the screen are no longer so important. However, in practice, this is not enough. AutoLayout is just like its name. It is only a method for Layout Based on constraints, and there is still a lack of experience for different devices. The most obvious problem is that it cannot determine different interactive experiences based on the device type. In many cases, you still need to determine whether the device is an iPhone or an iPad, and whether the current device direction is vertical or horizontal. In this case, it is difficult for us to completely get rid of the judgment and dependency on the device, and if there is a new size and device, this dependency is obviously very fragile (think about iWatch ..).
Therefore, in iOS 8, Apple overturned the original method from its original design philosophy and introduced a new set of ideas to adapt to the continuous development of devices. This is SizeClasses.
It is no longer differentiated by the specific size of the device screen, but divided into two categories (Regular and Compact) through their sensory performance ). Developers can ignore the specific size, but adapt these two types and their combinations. In this way, no matter in design or code, we can not be limited by the specific size, but turn to the visual senses that follow the size for adaptation.
SizeClasses has three values: Regular, Compact, and Any. What does Any mean? If weight is set to Any and height is set to Regular, the interface elements in this state will exist in either weight Regular or Compact State as long as the height is Regular. This relationship should be called an inheritance relationship. The four interface descriptions and the interface description that can be inherited are as follows:
1234 |
W: comppu: Compact inheritance (w: Anyh: Compact, w: comppu: Any, w: Anyh: Any) W: Regularh: Compact inheritance (w: Anyh: Compact, w: Regularh: Any, w: Anyh: Any) W: comppu: Regular inheritance (w: Anyh: Regular, w: comppu: Any, w: Anyh: Any) W: Regularh: Regular inheritance (w: Anyh: Regular, w: Regularh: Any, w: Anyh: Any) |
The size of so many devices (iPhone 4S, iPhone 5/5S, iPhone 6, iPhone 6 Plus, iPad, AppleWatch) is simply expressed through SizeClasses:
IPhone 4S, iPhone 5/5S, iPhone 6
Portrait screen: (w: comppu: Regular)
Horizontal screen: (w: comppu: Compact)
IPhone6Plus
Portrait screen: (w: comppu: Regular)
Horizontal screen: (w: Regularh: Compact)
IPad
Portrait screen: (w: Regularh: Regular)
Landscape screen :( w: Regularh: Regular)
AppleWatch)
Portrait screen: (w: comppu: Compact)
Horizontal screen: (w: comppu: Compact)
PS: attachment:
2. UITraitCollection and UITraitEnvironment (Size Classes handwritten code)
To characterize SizeClasses, Apple introduced a new class, UITraitCollection, in iOS 8. This class encapsulates information such as SizeClass in the horizontal and vertical directions. Most of the basic UI classes (including UIScreen, UIWindow, UIViewController, and UIView) in iOS 8's UIKit have implemented the UITraitEnvironment interface, through the traitCollection attribute, we can get the corresponding UITraitCollection object to know the current SizeClass, and further determine the layout of the interface.
Opposite to the responder chain in UIKit, traitCollection will be passed from top to bottom in viewhierarchy. For UI parts that do not specify a traitCollection, The traitCollection of its parent node is used. This is useful when the layout contains the interface of childViewController. Another useful method in the UITraitEnvironment interface is-traitCollectionDidChange :. This method is called when the traitCollection changes. In actual operations, we often overwrite-traitCollectionDidChange: Or-willTransitionToTraitCollection: withTransitionCoordinator: Method in ViewController. (For ViewController, the latter is also a better choice, the transfer context is provided to facilitate animation, but for normal views, only the previous method is provided.) then, the current traitCollection is judged and re-laid and animated. The code looks like this:
123456789101112 |
override func willTransitionToTraitCollection(newCollection: UITraitCollection, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator){ super .willTransitionToTraitCollection(newCollection, withTransitionCoordinator: coordinator) coordinator.animateAlongsideTransition({ (context: UIViewControllerTransitionCoordinatorContext!) -> Void in if (newCollection.verticalSizeClass == UIUserInterfaceSizeClass.Compact) { //To Do: modify something for compact vertical size } else { //To Do: modify something for other vertical size } self.view.setNeedsLayout() }, completion: nil) } |
In two To Do statements, we should delete or add or modify the AutoLayout constraint under different conditions (of course, you can Do anything else you want ), then call-setNeedsLayout to trigger the transfer animation in the context. If you insist on using code for processing, you may need to remove the old constraints and add new constraints for different SizeClasses, it can be said that it is very troublesome (at least I think it is troublesome ). However, if we use IB, all these tasks and code can be saved, we can easily specify various SizeClasses constraints in IB (we will introduce how to use IB to correspond to SizeClasses later ). In addition, using IB not only saves hundreds of lines of layout code, but also allows you to get a lot of design-time monitoring from the new Xcode and IB to view and Debug Features in real time. It can be said that the time consumption and cost gap between the handwritten UI and the Design Using IB are further widened, and many handwritten UIS cannot be implemented, but IB can complete the task without thinking about it. In this sense, the new IB and SizeClasses systems can say that hand-written code has been ruthlessly sentenced to a deadlock.
In addition, the introduction of new APIs and systems has also led to the death of old friends who are familiar with UIViewController. For example, the following APIs are all discarded:
123456 |
@property(nonatomic, readonly) UIInterfaceOrientation interfaceOrientation - willRotateToInterfaceOrientation:duration: - willAnimateRotationToInterfaceOrientation:duration: - didRotateFromInterfaceOrientation: - shouldAutomaticallyForwardRotationMethods |
Now all are unified to viewWillTransitionToSize: withTransitionCoordinator:. The concept of rotation is no longer promoted. In fact, taking a closer look, the so-called rotation is just a change in Size. We have been cheated by Apple for many years, haven't we?
3. Use SizeClasses in InterfaceBuilder
Create a common project. If you want to activate the sizeclasses option before creating an Xcode6 project. You can find it under the autolayout option in the attribute panel of InterfaceBuilder.
First, let's take a look at the sizeclass mesh in Xcode. This is an area where you can switch between different la S. When you view the storyboard, you can view the bottom of the view and click the tag 'wanyhany. You will see some images similar to the grid.
By default, we start with a basic setting, that is, anywidth and anyheight. Many things will be placed and changed here, including the default layout for all directions of the iphone and ipad. Apple recommends setting most settings on this interface. This is because it is especially simple to reduce the workload. Let's layout a super wide button in the middle of the screen. Give it a green background, so that we can see its real size, and give it a constraint to center it.
And give it an exaggerated fixed width of 600.
Now you can run both the ipad and iphone simulators. You will see that they are both centered, but the two directions for the iphone are too wide, (Here you set the width of the button on the page, but it is not updated immediately because you did not update the image when adding constraints. As a result, the storyboard is not updated, when the simulator is running, it is updated. There are warning instructions in the outline column on the left. You can click the Yellow triangle in the warning to update the picture, which is actually UpdataFrame)
Let's use sizeclasses for correction. Return to the grid of the first figure and select the portrait setting of the iphone, that is, the Compact width + the general height. The red rectangle in the grid.
You will notice that the bar at the bottom of the mesh changes to blue after you select it. It is warning you: "Hey, you are not in a basic setting. Some changes will only be displayed when you are running. So the bar is blue now !" Some of the changes I mentioned are due to four sizeclasses that you can change: 1 constraint constant, 2 font, 3 constraint on/off, and 4 subviews ON/OFF.
The first two are self-evident, but let me tell you how to make the latter two work. In the current sizeclass (compactwidth and regularheight) situation, let's try to close a constraint. In the outline bar of the document, click the CentreX calibration constraint set in our button:
Now let's take a look at our attribute check bar. At the bottom, we can see the labeled word "Installed", and there is an additional plus button on the left. Click the additional plus sign and click 'compactwidth | regularheight' (the current one is ).
Now you will see two markers. deselect the one you just added (wChR)
Now our constraints are no longer placed and we do anything to configure sizeclasses. As you can see, Xcode is complaining that our constraints are too messy (the outline on the left has an error message indicating that you are missing the constraints-the translator ), if the app is running on the iphone simulator at this time, the button is not centered in the X direction. But it is still centered on the ipad, because the constraints are still placed in the Basic settings. This constraint will always be configured unless we uncheck it. You can even rotate your iphone simulator and find that the button will return to the center magically, because the horizontal side of the iphone is different from the sizeclass configuration. Well, let's check it back, let the button return to center.
Now let's change the constraint we set in the button width, select the button, and go to the Size attribute check bar, drop down to the bottom, we can see all the constraints. Click Edit, whose Width is 600, and set it to 100:
Run on the iPhone simulator and you will see that the button has the correct width. The 600 width is displayed when the simulator is running on the ipad, because the width in the Basic settings is not changed. However, the landscape of the iphone is still not very good, because the landscape settings of the iphone come from the basic AnyAny settings. Let's fix it. In the grid, select compactWidth and CompactHeight. That is, the blue mesh of the first image.
Now we change the width constraint in this setting, just as we changed for compactxregular. Returns a 400 width. Run the iphone simulator and rotate it horizontally. The button is 400 in width and looks great. As we expected. One good thing is that you can see a list Of all constraints, which are set differently. Just select the constraint you want to see in the document outline, and then go to the attribute check bar. They are neatly arranged under the initial constant. It indicates each setting based on its application.
Even if we decide that we want to just remove the button in iphone landscape mode, we only need to reverse place views using sizeclasses, just like we put a constraint in reverse direction. Select our UIbutton and scroll to the bottom of the property checker. Click the plus sign to add a new placement option for our current settings, and deselect it.
As you can see, the view disappears immediately, because we reverse place it in the settings, and we can immediately see it. When you run the app, you can see that it disappears on the portrait portraitiphone, But it returns when you rotate to the landscape. Of course, it has always been placed on the ipad because the ipad still uses basic settings.
4. SizeClasses, ImageAsset, and UIAppearence
The support for SizeClasses is also added to ImageAsset, that is, we can specify different images for different SizeClass. Select an image on the editing panel of ImageAsset. The Inspector now has a combination of Width and Height, add the corresponding SizeClass, and drag the appropriate image, in this way, the SDK will replace the corresponding Size graph. In addition, you can select the corresponding size in IB to view the changes during editing.
It's really easy to do. But let's take a demo to explain it. For example, when the vertical direction Compact is implemented below, the smiling face is changed to a crying face-of course, no line of code is needed.
In addition, a renderingMode attribute is added to UIImage in iOS7. We can use imageWithRenderingMode: and input a suitable UIImageRenderingMode to specify whether the image is rendered in Template mode. In the new Xcode, you can use the RenderAs option in ImageAsset to specify whether the image is required as a template. In UIApperance, Apple also adds a method for SizeClasses. Using the + appearanceForTraitCollection method, we can easily set apperance for different trait applications. For example, in the above example, we want to make the smiling face green, and the crying face red, should not be too simple. First, set the rendering option in ImageAsset to TemplateImage, and then add the following two lines to AppDelegate:
12 |
UIView.appearanceForTraitCollection(UITraitCollection(verticalSizeClass:.Compact)).tintColor=UIColor.redColor() UIView.appearanceForTraitCollection(UITraitCollection(verticalSizeClass:.Regular)).tintColor=UIColor.greenColor() |
Complete, just drag the mouse, two lines of simple code, and then can change with joy, it is indeed a great success for everyone.