Sorry, this series cannot be done, only to bring you the most fulfilling and detailed iOS learning experience. Reading the full text may take 3 hours, maybe 30 minutes, or maybe 3 minutes, or even 3 seconds. Afterwards, I will analyze how many readers only persisted for 3 seconds and continue to improve.
Welcome back to the iOS series from "Xia Piao Piao" (3) — Common UI Components. This article mainly discusses the system architecture of iOS, important members of UIKit components, their basic usage and features, and the UI anatomy of "Xia Piao Piao".
iOS System Architecture#
The system architecture of iOS is mainly divided into 4 layers, from bottom to top: Core OS, Core Services, Media, and Cocoa Touch, and above that constitutes an Application.
Let's take a closer look at the main framework components of these 4 layers:
For beginners, the main focus should be on: the "UIKit framework" of the Cocoa Touch layer + the "Foundation framework" of the Core Services layer. The following introduction comes from Apple's developer documentation:
- UIKit: Build and manage graphical, event-driven user interfaces for your iOS or tvOS applications;
- Foundation: Access basic data types, collections, and operating system services to define the foundational functionality of applications.
You can refer to Appendix 1 and Appendix 2 for their framework composition.
Regarding Foundation, the main focus is on the encapsulation of basic data types, which can be referred to in Objective_C Foundation Framework — Yi Bai Tutorial; concerning UIKit, it is our main character today❗️
UIKit#
Below is the inheritance relationship of important members of UIKit and their respective roles:
- UIView — Displays content and provides interaction
- UIImageView — Displays images
- UILabel — Displays text
- UIScrollView — Displays content larger than the screen (e.g., maps)
- UITableView — Displays tabular content (e.g., phone books)
- UIViewController — Manages the container of Views
- UITabBarController — Manages the switching of multiple ViewControllers, tabbed switching method
- UINavigationController — Manages the switching of multiple ViewControllers, push/pop switching method
After having a rough understanding of them, let's do a little exercise. What UIView and UIViewController are mainly included in the following two animations:
If you can quickly identify their basic components, it indicates that you have a basic understanding of the roles of UIKit's foundational components.
So how should these basic components be used, and what are their features?
Let's first discuss two heavyweight members: UIView and UIViewController.
UIView#
Displays content and provides interaction
Basic Usage#
// 1)alloc: Request space for UIView; init: Initialize the object
UIView *view = [[UIView alloc] init];
// 2)Top-left corner coordinates (100, 100), width and height 100 * 100
view.frame = CGRectMake(100, 100, 100, 100);
// + Set background color
view.backgroundColor = [UIColor redColor]; // Equivalent to UIColor.redColor
// 3)Add the newly defined view to the parent view
[self.view addSubview:view];
The following 3 steps are necessary. (❗️: Components inheriting from UIView generally rely on these 3 steps)
1)Initialization: First, request space by calling the alloc class method of UIView, and initialize the object by calling the init method on the returned instance;
2)Set frame: That is, its top-left corner coordinates and width/height;
3)Add: Add the defined UIView object to a parent UIView object.
Features#
1)Stack structure manages subviews: It can add multiple subviews, with later added views displayed above earlier added ones, and the parent view can manage the view hierarchy of its subviews.
From the above image, it is easy to see the order in which the two squares were added, red first and then green.
⚠️:
- This feature may likely be one reason for interaction failure, as for overlapping Views, the interaction of the upper layer may cause the interaction of the lower layer to fail.
- Observant friends may wonder what self in the code represents; its view property refers to the second heavyweight member mentioned below.
As an object-oriented programming language, OC refers to the current object calling the method as self, and the type of self above is UIViewController.
UIViewController#
Manages the container of Views
If you understand the MVC pattern (see Appendix 3 at the end), the relationship with UIView becomes easy to understand: UIView is the V—View in MVC, and UIViewController is the C—Controller in MVC.
Basic Usage#
// 1)alloc: Request space for UIViewController; init: Initialize the object
UIViewController *viewController = [[UIViewController alloc] init];
// 2)Attach a view to the viewController's view
[viewController.view addSubview:someView];
1)Initialization
2)Add the UIView object to be managed
Features#
1)Acts as a container, containing a default view property of type UIView, which corresponds to the above❗️
RootView is a UIView object that comes with the UIViewController object.
2)Below is its lifecycle, where developers can choose to override and add custom operations at appropriate times.
- init -> loadView ->
- viewDidLoad -> viewWillAppear -> viewDidAppear ->
- viewWillDisappear -> viewDidDisappear ->
- dealloc
- This image is sourced from quanqingyang's CSDN blog, which clearly illustrates the process of the UIViewController object from initialization 👉 loading the view 👉 displaying the view 👉 view disappearance 👉 unloading the view 👉 destruction.
- Beginners generally focus on init and viewDidLoad, adding some custom object initialization during init, and adding custom subviews to self.view during viewDidLoad.
PS:
- When creating a new project, a ViewController (.h header file and .m source file) is automatically generated, which serves as the root ViewController for the entire project by default.
- Generally, we encapsulate some custom ViewControllers that inherit from UIViewController, adding specific views and interaction logic within them.
After discussing the two heavyweight members, let's take a short break and then look at the basic usage and characteristics of their descendants.
Descendants of UIView#
UIImageView#
Displays images
Basic Usage#
// 1)alloc: Request space for UIImageView; init: Initialize the object
UIImageView *imageView = [[UIImageView alloc] init];
// 2)Top-left corner coordinates (100, 100), width and height 100 * 100
imageView.frame = CGRectMake(100, 100, 100, 100);
// 3)Set the image fill mode
imageView.contentMode = UIViewContentModeScaleAspectFill;
// 4)Define the specified image as a UIImage object and assign it to the image property of imageView
#define kImageName @"./ford.jpg"
imageView.image = [UIImage imageNamed:kImageName];
1)Initialization
2)Set frame
3)Set the image fill mode contentMode
The main options are as follows:
The contentMode set in the above code is UIViewContentModeScaleAspectFill, corresponding to the second example in the above image, which is a commonly used method.
4)Assign a value to the image property
All images must first be encapsulated into UIImage objects before being displayed through UIImageView.
Features#
1)Displays animated images: Assign an array of UIImage types to the animationImages property of UIImageView;
2)Related third-party library: SDWebImage, which optimizes the process of generating UIImage, generally used for fetching network images.
PS: SDWebImage: https://github.com/SDWebImage/SDWebImage
UILabel#
Displays text
Basic Usage#
// 1)Initialization
UILabel *summaryTabLabel = [[UILabel alloc] init];
// 2)Top-left corner coordinates (100, 100), width and height 100 * 100
summaryTabLabel.frame = CGRectMake(100, 100, 100, 100);
// 3)Set text
summaryTabLabel.font = [UIFont systemFontOfSize:14];
summaryTabLabel.textAlignment = NSTextAlignmentCenter;
summaryTabLabel.textColor = [UIColor orangeColor];
[summaryTabLabel setText:@"Movie Summary"];
// PS: Set gesture
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(summaryTabTapAction)];
[summaryTabLabel addGestureRecognizer:tapGesture];
summaryTabLabel.userInteractionEnabled = YES;
1)Initialization
2)Set frame
3)Set text: font, alignment, text color, text content
PS: Set gesture
The tab bar (Movie Summary, Actor Information, More Information) in the details page of "Xia Piao Piao" is implemented based on UILabel, and by adding click gestures to them, they can achieve button-like click effects.
⚠️: The interaction of UILabel objects is disabled by default, so you need to set the userInteractionEnabled property to YES to enable it.
Features#
1)Multi-line display (as shown in the content of each tab in the above animation)
summaryTabLabel.numberOfLines = 0; // Default is 1
[summaryTabLabel sizeToFit];
2)Text truncation methods: lineBreakMode property, such as breaking lines by characters, omitting parts in between, etc.
3)Display complex text: use NSAttributedString type, YYText (third-party library).
UIScrollView#
Displays content larger than the screen
Basic Usage#
// 1)Initialization and set visible range — frame
UIScrollView *detailScrollView = [[UIScrollView alloc] initWithFrame:self.view.frame];
// 2)Set scroll range — contentSize
#define kScreenWidth UIScreen.mainScreen.bounds.size.width
#define kScreenHeight UIScreen.mainScreen.bounds.size.height
detailScrollView.contentSize = CGSizeMake(kScreenWidth * 3, kScreenHeight);
// PS: Whether to enable paging scroll
detailScrollView.pagingEnabled = YES;
1)Initialization and set frame
The frame is the visible range of this view, which can be set during initialization using initWithFrame instead of init.
The value assigned to self.view.frame in the above code is the frame of self.view, which you should know what it is from earlier.
2)Set contentSize
contentSize is the scroll range of this view, in other words, its entire range.
PS: Set whether scrolling is page-based: pagingEnabled property
Looking at this animation from "Xia Piao Piao," its contentSize is three times the screen width, with paging scroll enabled.
Features#
1)frame and contentSize
The former is the visible range, and the latter is the scroll range.
These two are the most important properties when initializing UIScrollView, and incorrect settings may prevent scrolling.
2)setContentOffset method
[detailScrollView setContentOffset:CGPointMake(detailScrollView.bounds.size.width * 2, 0) animated:YES];
In the previous section, when clicking the tab bar triggers the scrolling of UIScrollView, the setContentOffset method is needed in the event summaryTabTapAction corresponding to the UILabel click gesture.
Next is the youngest descendant of UIView, which inherits from UIScrollView and is also the most difficult to understand here.
UITableView : UIScrollView#
Displays tabular data
Basic Usage#
// 1)Initialization and set visible range — frame
UITableView *homeTableView = [[UITableView alloc] initWithFrame:self.view.bounds];
// 2)Specify dataSource and delegate (remember to add two protocols in the class declaration)
homeTableView.dataSource = self;
homeTableView.delegate = self;
// 3)Next, implement the methods in the protocols (@required must-implement items, @optional optional items)
1)Initialization and set frame
The frame refers to the visible range, similar to UIScrollView.
2)Specify the dataSource and delegate
dataSource and delegate are two protocols of UITableView. "Specify the delegate" means allowing "others" to comply with this protocol, to help perform some tasks in the protocol, some of which are mandatory and some optional; it can be likened to labor contracts or rental agreements in our daily lives.
i. dataSource is responsible for the data source of the table, cell content, etc.;
ii. delegate is responsible for cell interaction, configuration, etc.
It is also important to clarify that the above code is written in the Controller, where self refers to an object of type UIViewController. This means that self complies with these two protocols and needs to implement the methods in the protocols.
3)Implement the methods in the protocols
i. dataSource
#pragma mark - UITableViewDataSource
// @required
// 1)Set the number of Cells buffered in each Section
(NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section {
return 3;
}
// 2)Render the content of each Cell
(UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// 1. Find the cell corresponding to kCellId from the reuse pool (#define kCellId @"id1")
// [tableView dequeueReusableCellWithIdentifier:kCellId];
// 1-2. If it does not exist, manually generate the corresponding cell and register it as kCellId
// [… reuseIdentifier:kCellId];
// 2. Set cell data
// 3. return cell
}
// @optional…
The dataSource protocol has 2 @required
(must-implement) methods and several @optional
(optional implementation) methods.
The first @required
method is used to set the number of Cells buffered in each Section, where return 3
indicates rendering only 3 pieces of data. In actual use, it is generally determined based on the number of data returned from a network request.
The second @required
method is used to set the content rendered for each cell, generally divided into 3 steps:
- Find the cell corresponding to
kCellId
(custom cell identifier) from the reuse pool. If it does not exist, manually generate the corresponding cell and register its identifier in the reuse pool. - Set the cell data.
- Return the cell.
PS: Regarding the reuse pool, we will mention it again shortly. UITableViewCell type inherits from UIView, and its basic usage and features are largely similar.
ii. delegate
#pragma mark - UITableViewDelegate
// @optional
// 1)Set the height of each Cell
(CGFloat)tableView:(UITableView *)tableView
heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 100;
// 2)Set the event after clicking the Cell
(void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// do Something after cell selected
}
// …
The delegate protocol has several @optional
(optional implementation) methods, and the above lists two commonly used methods to implement.
The first @optional
method is used to set the height of each Cell.
The second @optional
method is used to set the event after clicking a Cell, such as pushing another page.
Referencing the homepage of "Xia Piao Piao," you can feel the role of the above four methods to be implemented:
The number of movies in this movie table is determined by the data from the network request; the format of each Cell can be reused; the row height is fixed; the screen after clicking on a movie is left to your imagination.
Features#
1)UITableView only handles display; data, Cell, and interaction need to be provided by the developer.
i. dataSource: data source, Cell content, etc.
ii. delegate: Cell interaction, configuration, etc.
2)Reuse pool
Since generating and destroying Cells is performance-intensive, the reuse pool mechanism is implemented, which is based on a double-ended queue (dequeue). When scrolling up the screen, the cells above disappear, automatically recycled by the system, and the cells below need to be loaded. First, search the reuse pool for its unique identifier id. If found, it can be successfully reused; otherwise, manually generate the corresponding Cell and register it in the reuse pool, binding the id.
You can appreciate the essence of the reuse pool from the following image:
As you scroll up, the newly loaded Cells not only reuse the Cell style but also reuse the green checkmark ✅ on the right.
In actual use, this can be somewhat amusing, so we generally set its data after reusing the Cell, as mentioned in the third step of basic usage; or override the prepareForReuse
method to clear unnecessary data before each Cell reuse. (Note: prepareForReuse
is a method of UITableViewCell.)
Now, let's further understand the complete composition of UITableView:
The demo of "Xia Piao Piao" we just showcased is only one section and does not set headers and footers.
In fact, a complete UITableView can refer to the above image, expressed in the formula as follows:
UITableView = tableHeaderView + n ✖️ Section + tableFooterView
Where Section = sectionHeader + n ✖️ UITableViewCell + sectionFooter
Finally, finally, have you managed to stick around to this point? "Descendants of UIView" check-in point, 💳 ding~
Next are the two promising children of UIViewController.
Children of UIViewController#
These two children share the same point: they can manage the switching of multiple UIViewController objects, which sounds like the son can also manage multiple fathers~ 😛
The differences can be felt from the following introductions:
UITabBarController#
Basic Usage#
// 1)Initialization
UITabBarController *tabbarController = [[UITabBarController alloc] init];
// 2)Set the multiple Controllers to be managed (belonging to or inheriting from UIViewController)
[tabbarController setViewControllers:@[controller1, controller2, controller3]];
1)Initialization
2)Set the multiple Controllers to be managed
This is set up at the beginning, and each Controller's type belongs to or inherits from UIViewController.
Let's take a look at its demo:
This UITabBarController object manages three Controllers.
Features#
1)UITabBar, the switching bar at the bottom of the page:
The managed UIViewController objects modify the content of the corresponding Tab on the UITabBar themselves:
controller1.tabBarItem.title = @"News";
controller1.tabBarItem.image = [UIImage imageNamed:@"tab1.png"];
Such as icons, titles, etc.
UINavigationController#
Basic Usage#
// 1)Initialization and set the root Controller
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:controller1];
// 2)Push another Controller at the appropriate time
[navigationController pushViewController:controller4 animated:YES];
1)Initialization and set the root Controller
Only one needs to be set.
2)Then push another Controller at the appropriate time
For example, in "Xia Piao Piao," clicking on a movie on the homepage pushes the corresponding detail page Controller.
Let's take a look at its demo:
When clicking the Show button, another Controller is pushed.
Features#
1)UINavigationBar, the navigation bar at the top of the page:
Similarly, the UIViewController objects modify the content of their page's UINavigationBar themselves:
controller4.navigationItem.title = @"Content";
Such as titles, etc., and can also customize buttons on both sides.
2)Stack management: You can return to the previous level or return to the root view or a specified view.
Through the above discussion, do you understand the differences between UITabBarController and UINavigationController?
Next, we will extend two knowledge points, a segment that good students love — it can also deepen the understanding of the above content.
Extension 1: Common Page Switching Methods#
From the two types of Controllers just mentioned, you can sense two different page switching methods. So what are the common page switching methods?
- tabs: You know who this refers to~
- push / pop: You know who this refers to~
- present / dismiss: Unlike the second method, it is generally used for switching between different business interfaces and can only return step by step.
- show (iOS 8.0+)
This method automatically selects the switching method based on the type of ViewController, as shown in the following image for splitViewController's switching method:
Generally seen on iPad devices, such as the Taobao App.
Extension 2: Two Nesting Methods#
In practical use, UITabBarController and UINavigationController are often used together. Rather than saying it is a combination method, it is more accurate to say it is a nesting method.
Method One:
Method Two:
The most obvious difference between these two methods is whether the TabBar disappears during page switching.
But why does Apple officially recommend the first method? In summary, there are probably two points:
1)Higher freedom, each NavigationController is independent, and you can choose not to nest NavigationController;
2)Can achieve the same page switching effect as the second method by manually hiding the Tab bar.
Note: UI Rendering is on the Main Thread#
Today, we have extensively introduced the important components of UIKit, so do not forget this last reminder during their use: UI rendering is all done on the main thread❗️
As for why? Here are some excerpts from Why UI Must Be Operated on the Main Thread — a Juejin article.
1)UIKit is a thread-unsafe class. UI operations involve accessing various View object's properties, and if asynchronous operations are performed, there may be read/write issues; if locks are added, it will consume a lot of resources and slow down the running speed;
2)Only on the main thread can it respond to events. The starting point of the entire program, UIApplication, is initialized on the main thread, and all user events are transmitted on the main thread (such as clicks, drags), so UI can only respond to events on the main thread;
3)Ensure the synchronous update of images. In terms of rendering, since image rendering needs to be updated on the screen at a refresh rate of 60 frames, it cannot ensure that this processing can achieve synchronous updates in non-main thread asynchronous situations.
Therefore, when performing UI rendering, always pay attention to whether the current thread is the main thread!
UI Anatomy of "Xia Piao Piao"#
Now, let's take a look at the UIKit components that make up "Xia Piao Piao," directly showing the image!
👏 Did you gain a lot today? If you can reproduce the above image, then your grasp of UIKit must be quite good! Feel free to leave a message to share your thoughts or any questions you may have.
See you next week#
Next week, we will discuss debugging techniques in Xcode, so stay tuned.
Appendix#
1)Foundation
2)UIKit
By observing the parent classes of components, you can easily locate the functions of the components.
3)MVC Pattern
The MVC pattern is a software architectural pattern, where MVC is an acronym for three words: Model, View, and Controller:
1)Model is the data or information that the program needs to operate.
2)View is the user interface provided for operation, the shell of the program.
3)Controller is in between the above two, selecting data from the Model based on the commands input by the user in the View and performing corresponding operations to produce the final result.
- Reference Discussing the MVC Pattern — Ruanyifeng