A brief discussion on iOS screen adaptation practice

A brief discussion on iOS screen adaptation practice

Screen adaptation in front-end development is actually a basic skill, and every programmer has his own summary through long-term practice.

On the iOS platform, I personally feel that Apple's support for adaptation is not very user-friendly. It provides technologies such as AutoLayout and sizeClass, which are not as flexible as front-end technologies such as flexBox. It seems that Apple has chosen the wrong skill tree, and it pays too much attention to using xib to configure the UI, but many programmers are still used to pure code programming. Cocoa does not have pure layout files such as css, which often leads to us writing layout, UI and logic together, which is very confusing and lengthy.

The following is a brief introduction to the direction and ideas of screen adaptation in practice, just to stimulate further discussion.

From Design to Code: Communication and Standards

The UI interface of the App is drawn by designers (product, UI) and then implemented by developers. Both parties must have good communication and standardize and document the design content.

For designers, the rules of adaptation are always in their minds. Whether it is proportional scaling, fixed spacing, a common set of rules, or a special layout for large screens, all need to be conveyed to the honest coders in a clear way.

The most common layout methods are:

  • Fixed spacing: The spacing is always fixed at different sizes.
  • Flow layout: text, pictures, etc. are arranged in a flow-like manner on different screens. For example, four pictures are displayed in a row on a large screen and three pictures are displayed in a row on a small screen. The picture size is fixed.
  • Proportional enlargement: Proportional enlargement of spacing, text size, image size, etc.
  • Maintain ratio: The length and width of two UI elements or images should maintain a certain ratio.
  • Alignment: Elements are aligned in a certain direction.

Designers need to clearly mark these layout rules to facilitate communication and future tracing.

For some common UI components, standardization is necessary, which is conducive to the unification of App style in design and convenient for development and encapsulation in implementation.

UI construction: xib VS pure code

Apple has always used xib to promote their app development as being easy to use: just drag and drop everything you need onto the screen, and a UI interface is ready. Isn't that cool?

The advantages of Xib are obvious:

  • Easy to use, visual, what you see is what you get
  • Reduce the amount of code
  • Fast, suitable for rapid development of small apps

However, in our actual project, it is not recommended to use xib.

First of all, xib itself is too clumsy and can only build some simple UIs. It has poor dynamics and cannot meet the complex UI interaction requirements of App.

Secondly, those who have done performance optimization know that the performance of xib (or StoryBoard) is very poor. Compared with components allocating with pure code, xib loads slowly and takes up the size of the App package. It's not just the performance of the App. When you use an old Mac to open a large xib file, it can sometimes make you question your life and seriously affect your development efficiency (mood).

In addition, xib is not a good option for team collaboration: it is difficult to read, historical changes cannot be viewed on git, conflicts are prone to occur and are difficult to resolve, the links between elements and code through outlets are difficult to maintain, and errors are prone to occur in changes, etc.

In addition, for an engineer like me who switched to the front-end midway, I have a mysterious distrust of everything configured on the IDE interface, and I feel that it is not as reliable as lines of black-and-white code.

Of course, we don't completely disable xib. The disadvantages of coding UI are also obvious: cumbersome and large amount of code. Therefore, for some UI components with many elements and relatively fixed, we can use xib to reduce the amount of code:

In the case of cumbersome UI code and a lot of repeated coding, we can clarify the logic through appropriate encapsulation (UI factory class) and organizational structure (MVC, separation of UI code).

  1. // label factory method
  2. + (UILabel *)labelWithFont:(UIFont *)font
  3. color:(UIColor *)
  4. text:(NSString *)text
  5. attributeText:(NSAttributeString *)attributeText
  6. alignment:(NSTextAlignment)alignment;

Layout: Back to Basics

Apple introduced AutoLayout in Cocoa platform for basic UI layout since iOS 7. However, AutoLayout is very anti-human, not only the code is cumbersome, but also the usage is inflexible and has many restrictions.

For example, if I want to display three elements on the screen with equal spacing, it would be a complete failure if I used AutoLayout, not to mention the advanced requirement of dynamically switching between two layouts.

Later, Apple launched sizeClass in an attempt to solve the problem of multiple layouts, but it still did not address the pain points of programmers, and its reliance on xib made it less versatile.

A typical AutoLayout code is as follows:

  1. _topViewTopPositionConstraint = [NSLayoutConstraint
  2. constraintWithItem:_topInfoView
  3. attribute:NSLayoutAttributeTop
  4. relatedBy:NSLayoutRelationEqual
  5. toItem:self. view  
  6. attribute:NSLayoutAttributeTop
  7. multiplier:1.0
  8. constant:self.navigationController.navigationBar.frame. size .height + self.navigationController.navigationBar.frame.origin.y];
  9.     
  10. [self. view addConstraint:topViewLeftPositionConstraint];
  11.     
  12. (The similar structure above is omitted here*4)

A lot of code is omitted above, and in fact it can't fit on one page. What does it do? It just places an element close to the edge of the screen. In the project, we will use the third-party AutoLayout encapsulation: PureLayout, which simplifies the code and has other practical functions.

AutoLayout is suitable for:

  • Basic alignment (up, down, left, right, center, etc.)
  • Fixed layout, fixed spacing, and low dynamics
  • Simple and few UI elements

Not good at:

  • Proportional layout
  • A more dynamic part of the page
  • Adaptation to different screen size ratios
  • Complex UI

Another point is that AutoLayout has a lossy effect on performance, so in scenarios that require performance, such as cells in a list, we will use code to calculate the frame and increase the sliding frame rate.

Therefore, in actual projects, it is necessary to choose a layout method.

Here is the layout code snippet of the homepage news feeds in the App:

  1. - (void)layoutSubviews {
  2.      
  3. [super layoutSubviews];
  4.      
  5. CGFloat cellWidth = CGRectGetWidth(self.bounds);
  6. CGFloat currentY = 0.f;
  7.      
  8. // 0.content
  9. CGFloat cellHeight = CGRectGetHeight(self.bounds);
  10. CGFloat contentHeigth = cellHeight - kCellPaddingHeight;
  11. _mainContentView.frame = CGRectMake(0, 0, cellWidth, contentHeigth);
  12.      
  13. // 1. topic
  14. CGFloat topicLabelWidth = [_topicLabel.text boundingRectWithSize:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:_topicLabel.font} context:nil]. size .width;
  15.      
  16. CGFloat topicLabelHeight = [@ "Height measurement" boundingRectWithSize:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:_topicLabel.font} context:nil]. size .height;
  17.      
  18. CGFloat topicLogoLeftPadding = 3.f;
  19. CGFloat topicLogoWidth = 10.f;
  20. CGFloat topicLeftPadding = 13.f;
  21.      
  22. _topicView.frame = CGRectMake(topicLeftPadding, currentY + kTopicUpPadding, topicLogoWidth + topicLogoLeftPadding + topicLabelWidth, topicLabelHeight);
  23. _topicLogo.frame = CGRectMake(topicLabelWidth + topicLogoLeftPadding, CGRectGetHeight(_topicView.frame) / 2.0 - topicLogoWidth / 2.0, topicLogoWidth, topicLogoWidth);
  24. _topicLabel.frame = CGRectMake(0, 0, topicLabelWidth, topicLabelHeight);
  25.      
  26. (Omitted a lot of code...)
  27.      
  28. // 10._sourceLabel
  29. CGSize sourceSize = [_sourceLabel.text boundingRectWithSize:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:_sourceLabel.font} context:nil]. size ;
  30.      
  31. _sourceLabel.frame = CGRectMake(kEdgeHorizontalPadding, currentY + kLeadingUpPading, sourceSize.width, sourceSize.height);
  32. }

As you can see, in order to determine the position of each element, we need to perform a lot of calculations, and the code is not easy to read and is cumbersome. If dynamicity is introduced, such as changing the font size of different screens and scaling the element size proportionally, the amount of calculation will increase by another order of magnitude.

Dynamic layout: clear and independent

The UI interface is dynamic. In different states, different sizes, or horizontal and vertical screen situations of mobile phones, we often need to switch between multiple layout solutions or fine-tune the layout. If you use xib layout, you can use the SizeClass + AutoLayout solution; if it is a page implemented by code, there is no official tool provided, and you can only use logic to judge.

Generally speaking, when we write complex UI pages, we need to follow two principles:

  1. The UI layout code should be clear: This is the most important thing. You should know at a glance what to adjust and how to adjust it. If you can't, split it appropriately and optimize the naming.
  2. Layout code should be independent of business logic: In some common design patterns, we will decouple the UI and data model. Within the UI, we should also decouple the interaction and configuration logic from the layout, and separate pure layout files such as front-end css.

Extract the layout code and call different implementations at different sizes:

  1. if (IS_IPHONE_6){
  2. self.layout = [MyLayout iPhone6Layout];
  3. } else if (IS_IPHONE_6_PLUS){
  4. self.layout = [MyLayout iPhone6PlusLayout];
  5. }
  6.  
  7. // Implement small screen layout
  8. + (MyLayout *)iPhone6Layout {...}
  9. // Implement large screen layout
  10. + (MyLayout *)iPhone6PlusLayout {...}

Font adaptation: font set

In development, we often encounter situations where we need to set fonts dynamically:

  • Different screen sizes, or horizontal and vertical screens, need to display different font sizes.
  • Provides users with the option to adjust the font size of articles.
  • Different language versions of the app require different display fonts.

A simpler approach is to use macros or enumerations to define font parameters. We get different values ​​for screens of different sizes:

  1. #ifdef IPHONE6
  2. #define kChatFontSize 16.f
  3. # else IPHONE6Plus
  4. #define kChatFontSize 18.f
  5. #endif

When adapting and expanding the fonts of some old codes, directly modifying the source code will cause too many changes and may cause confusion. You can use the runTime method to hack the display of controls such as Label and replace the original setFont method:

  1. + (void) load {
  2.      
  3. Method newMethod = class_getClassMethod([self class], @selector(mySystemFontOfSize:));
  4. Method method = class_getClassMethod([self class], @selector(systemFontOfSize:));
  5. method_exchangeImplementations(newMethod, method);
  6. }
  7.    
  8. + (UIFont *)mySystemFontOfSize:(CGFloat)fontSize{
  9. UIFont *newFont = nil;
  10. if (IS_IPHONE_6){
  11. newFont = [UIFont adjustFont:fontSize * IPHONE6_INCREMENT];
  12. } else if (IS_IPHONE_6_PLUS){
  13. newFont = [UIFont adjustFont:fontSize * IPHONE6PLUS_INCREMENT];
  14. } else {
  15. newFont = [UIFont adjustFont:fontSize];
  16. }
  17. return newFont;
  18. }

The above routines have obvious shortcomings: they are not flexible enough, the logic is dispersed, they are not easy to maintain, and their scalability is not good.

A better practice is to introduce the concept of font collection. What is a font collection? When we use Keynote or Office, the software will provide some paragraph styles, which define the fonts of paragraphs, titles, descriptions and other texts. We can switch between different paragraph styles to directly change the font style of the entire article.

Does it sound similar to our needs? We also do similar things in the code, defining fonts for different scenarios into a Font Collection:

  1. @protocol XRFontCollectionProtocol <NSObject>
  2.  
  3. - (UIFont *)bodyFont; // article
  4. - (UIFont *)chatFont; // Chat
  5. - (UIFont *)titleFont; // title
  6. - (UIFont *)noteFont; // description
  7. ......
  8. @ end  

Different scenarios, flexibly choose different font sets:

  1. + (id<XRFontCollectionProtocol>)currentFontCollection {  
  2. #ifdef IS_IPhone6
  3. return [self collectionForIPhone6];
  4. #elif IS_IPhone6p
  5. return [self collectionForIPhone6Plus];
  6. #endif
  7. return nil;
  8. }
  9.  
  10. // set font
  11. titleLabel.font = [[XRFontManager currentFontCollection] titleFont];

To adapt to new screens or scenes, we only need to simply add a set of fonts. It is very convenient to manage the font styles in the App, and it is also easy to switch dynamically.

In summary, it is relatively simple to implement a design draft in one size with code, but to faithfully reflect the design idea at various sizes requires reasonable code design and a certain amount of code.

UI restoration is actually a very important part of front-end development. As programmers, we often focus on the stability of the code and the normal use of the business, but ignore the equally important user experience factor of the software interface. If a designer sees that the proportions, fonts, and colors he has carefully adjusted are displayed crookedly on mobile phones of different sizes, he will definitely be very angry.

The author is a mobile development engineer at Xingren, a former embedded engineer, and focuses on new trends in big front-end technology.

<<:  iPhone X operation becomes more complicated: Apple is helpless but determined

>>:  Facebook adds new open source features for Android programmers and significantly modifies Buck!

Recommend

The bigger the mosquito bite, the more poisonous the mosquito is?

It is the golden season of "being sucked blo...

How to write an operational activity planning and promotion plan?

The article draws on the event operation in Teach...

.NET and cloud computing: integrated applications and best practices

With the rapid development of cloud computing, mo...

Why are your conversions always so low?

As bidders, we may also be familiar with the term...

Introduction and display styles of iQiyi advertising!

1. Introduction to iQiyi Promotion Platform As a ...

User Operations: 5 Case Studies to Teach You How to Insight into User Needs

The most difficult thing is to write an introduct...

WWDC2015 animation effects

Every year, Apple holds a major conference. WWDC ...