功能:使用 UITableView
實現(xiàn)一個名為 Homepwner 的應(yīng)用,用來管理財產(chǎn)清單,通過 UITableView
對象顯示一組 BNRItem
對象,實現(xiàn)表格行的添加、刪除和移動操作。
要點:UITableView
關(guān)于創(chuàng)建 Empty Application 空應(yīng)用模板
很多老版的 iOS 入門教程會在創(chuàng)建新項目時使用 Empty Application 模板,因為空應(yīng)用模板幾乎沒有多余的代碼,而其他模板會生成很多通用的代碼。這些代碼雖然能幫助開發(fā)應(yīng)用,但是對于初學(xué)者弊大于利。
而蘋果在 Xcode6 開始就移除了 Empty Application 模板,因此我們無法直接創(chuàng)建 Empty Application 模板,但是可以通過先創(chuàng)建一個 Single View Application 模板,再修改一下就可以達(dá)到此目的:
在 Xcode 中創(chuàng)建一個 Single View Application 模板;
-
刪除項目中的 Main.storyboard 和 LaunchScreen.storyboard 這兩個 XIB 文件(鼠標(biāo)選中并右擊Delete);
-
在 info.plist 配置文件中刪除
Launch screen interface file base name
和Main storyboard file base name
這兩項(選中該行,鼠標(biāo)點擊中間的灰白色減號按鈕)
-
打開
AppDelegate.m
文件,在委托方法application:didFinishLaunchingWithOptions:
中修改如下:Objective-C:
// 在此之前需要先導(dǎo)入根視圖控制器的頭文件: #import "ViewController.h" - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // 創(chuàng)建 UIWindow 對象 self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; // 設(shè)置 UIWindow 對象的根視圖控制器 ViewController *viewController = [[ViewController alloc] init]; self.window.rootViewController = viewController; // 設(shè)置窗口背景色為白色 self.window.backgroundColor = [UIColor whiteColor]; // 設(shè)置窗口可見 [self.window makeKeyAndVisible]; return YES; }
Swift 3:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { self.window = UIWindow(frame: UIScreen.main.bounds) self.window?.backgroundColor = UIColor.white self.window?.makeKeyAndVisible() return true }
參考:
- How to create an Empty Application in Xcode 6 without Storyboard @stackoverflow
- Xcode 7 創(chuàng)建 Empty Application 工程
(一)TableView
MVC設(shè)計模式
MVC(Model-View-Controller) 是模型-視圖-控制器設(shè)計模式。其含義是,應(yīng)用創(chuàng)建的任何一個對象,其類型必定是以下三種類型中的一種:
- 模型:負(fù)責(zé)存儲數(shù)據(jù),與用戶界面無關(guān)。
- 視圖:負(fù)責(zé)顯示界面,與模型對象無關(guān)。
- 控制器:負(fù)責(zé)確保視圖對象和模型對象的數(shù)據(jù)保持一致。
UITableViewController
視圖控制對象:該應(yīng)用采用 MVC 的設(shè)計模式,UITableView
是視圖,因此要通過視圖控制對象來創(chuàng)建和釋放 UITableView
視圖對象,并負(fù)責(zé)顯示或隱藏視圖。
數(shù)據(jù)源:UITableView
對象要有數(shù)據(jù)源才能正常工作。UITableView
對象會向數(shù)據(jù)源查詢要顯示的行數(shù)、顯示表格行所需要的數(shù)據(jù)和其他所需的數(shù)據(jù)。沒有數(shù)據(jù)的 UITableView
對象只是空殼。凡是遵守 <UITableViewDataSource>
協(xié)議的 Objective-C 對象,都可以成為 UITableView
對象的數(shù)據(jù)源(即dataSource
屬性所指向的對象)。
委托對象:還要為 UITableView
對象設(shè)置委托對象,以便能在該對象發(fā)生特定事件時做出相應(yīng)的處理。凡是遵守 <UITableViewDelegate>
協(xié)議的對象,都可以成為 UITableView
對象的委托對象。
UITableViewController
對象可以扮演以上全部角色,包括 視圖控制對象、數(shù)據(jù)源和委托對象。
UITableViewController
對象是 UIViewController
的子類,所以也有 view
屬性。 UITableViewController
對象的 view
屬性指向一個 UITableView
對象,并且這個 UITableView
對象由 UITableViewController
對象負(fù)責(zé)設(shè)置和顯示。 UITableViewController
對象會在創(chuàng)建 UlTableView
對象后,為這個 UITableView
對象的 dataSource
和 delegate
賦值,并指向自己。
1. 創(chuàng)建 UITableViewController 子類:HQLItemsViewController
#import <UIKit/UIKit.h>
@interface HQLItemsViewController : UITableViewController
@end
Tips: 如果你創(chuàng)建了一個 UITableViewController
的子類對象,那么就不需要再顯式地聲明該對象需要遵守 dataSource
和 delegate
協(xié)議了,因為它是默認(rèn)遵守的,你只需要去實現(xiàn)協(xié)議方法即可。
self.tableView.dataSource = self;
self.tableView.delegate = self;
2. 覆蓋父類的指定初始化方法 initWithStyle:
,將指定初始化方法改為init:
// 1?? 在【新的指定初始化方法】中調(diào)用父類的指定初始化方法;
-(instancetype) init {
//調(diào)用父類的指定初始化方法
self = [super initWithStyle:UITableViewStylePlain];
return self;
}
// 2?? 覆蓋父類的指定初始化方法,調(diào)用【新的指定初始化方法】。
- (instancetype) initWithStyle:(UITableViewStyle)style {
return [self init];
}
在 HQLItemsViewController.m
文件中實現(xiàn)以上兩個初始化方法后,可以確保無論向新創(chuàng)建的 HQLItemsViewController
對象發(fā)送哪一個初始化方法,初始化后的對象都會使用 UITableViewStylePlain
風(fēng)格。
3.創(chuàng)建 HQLItemsViewController 對象
在 AppDelegate.m
文件中導(dǎo)入 HQLItemsViewController.h
文件并初始化創(chuàng)建 HQLItemsViewController
對象。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// 創(chuàng)建TableView視圖控制器
HQLItemsViewController *itemsViewController = [[HQLItemsViewController alloc] init];
self.window.rootViewController = itemsViewController;
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}
4.為UITableView 對象設(shè)置內(nèi)容
這里從外部導(dǎo)入了一個寫好的 HQLItem 類的頭文件和實現(xiàn)文件(HQLItem.h 和 HQLItem.m),該類用于生成一組隨機(jī)數(shù)據(jù)。
//
// HQLItem.h
// 2.1 RandomItems
//
//
/**
* 該對象表示某人在真實世界擁有的一件物品
*
*/
#import <Foundation/Foundation.h>
// 頭文件聲明順序:實例變量、類方法、初始化方法、其他方法
@interface Item : NSObject
// 名稱
@property (nonatomic, copy) NSString *itemName;
// 序列號
@property (nonatomic, copy) NSString *serialNumber;
// 價值
@property (nonatomic) int valueInDollars;
// 創(chuàng)建日期
@property (nonatomic, readonly, strong) NSDate *dateCreated;
// 照片的key
@property (nonatomic, copy) NSString *itemKey;
//類方法
+ (instancetype)randomItem;
// Item類的指定初始化方法
// instancetype,該關(guān)鍵字表示的返回值類型和調(diào)用方法的類型相同,
// init方法的返回值類型都聲明為instancetype
- (instancetype)initWithItemName:(NSString *)name
valueInDollars:(int)value
serialNumber:(NSString *)sNumber;
// 其他初始化方法
- (instancetype)initwithName:(NSString *)name serialNumber:(NSString *)sNumber;
- (instancetype)initWithItemName:(NSString *)name;
@end
//
// HQLItem.m
// 2.1 RandomItems
//
//
// #import 可以確保不會重復(fù)導(dǎo)入同一個文件
#import "Item.h"
@implementation Item
// 類方法
+ (instancetype)randomItem{
//創(chuàng)建不可變數(shù)組對象,包含三個形容詞
NSArray *randomAdjectiveList = @[@"Fluffy",@"Rusty",@"Shiny"];
//創(chuàng)建三個不可變數(shù)組對象,包含三個名詞
NSArray *randomNounList = @[@"Bear",@"Spark",@"Mac"];
//根據(jù)數(shù)組對象所含對象的個數(shù),得到隨機(jī)索引
//注意:運(yùn)算符%是模運(yùn)算符,運(yùn)算后得到的是余數(shù)
//因此 adjectiveIndex 是一個0到2(包括2)的隨機(jī)數(shù)
NSInteger adjectiveIndex = arc4random() % [randomAdjectiveList count];
NSInteger nounIndex = arc4random() % [randomNounList count];
NSString *randomName = [NSString stringWithFormat:@"%@%@",
randomAdjectiveList [adjectiveIndex],
randomNounList [nounIndex]];
int randomValue = arc4random() % 100;
NSString *randomSerialNumber = [NSString stringWithFormat:@"%C%C%C%C%C",
(unichar)('0'+arc4random() % 10),
(unichar)('A'+arc4random() % 26),
(unichar)('0'+arc4random() % 10),
(unichar)('A'+arc4random() % 26),
(unichar)('0'+arc4random() % 10)];
Item *newItem = [[self alloc] initWithItemName:randomName
valueInDollars:randomValue
serialNumber:randomSerialNumber];
return newItem;
}
// 串聯(lián)(chain)使用初始化方法
- (instancetype)initWithItemName:(NSString *)name
valueInDollars:(int)value
serialNumber:(NSString *)sNumber {
self = [super init];
//if(self):父類的指定初始化方法是否成功創(chuàng)建了父類對象?
if(self){
_itemName = name;
_serialNumber = sNumber;
_valueInDollars = value;
// 設(shè)置_dateCreated的值為系統(tǒng)當(dāng)前時間
_dateCreated = [[NSDate alloc] init];
// 創(chuàng)建一個 NSUUID 對象,然后獲取其 NSString 類型的值
NSUUID *uuid = [[NSUUID alloc] init];
NSString *key = [uuid UUIDString];
_itemKey = key;
}
//返回初始化后的對象的新地址
return self;
}
- (instancetype)initwithName:(NSString *)name
serialNumber:(NSString *)sNumber {
return [self initWithItemName:name
valueInDollars:0
serialNumber:sNumber];
}
- (instancetype)initWithItemName:(NSString *)name {
// 調(diào)用指定初始化方法
return [self initWithItemName:name
valueInDollars:0
serialNumber:@""];
}
- (instancetype)init {
return [self initWithItemName:@"Item"];
}
// 覆寫 description 方法
// %@,對應(yīng)的實參類型是指向任何一種對象的指針,首先返回的是該實參的description消息
- (NSString *)description {
NSString *descriptionString =
[[NSString alloc] initWithFormat:@"%@(%@): ,Worth $%d ,recorded on %@",
self.itemName,
self.serialNumber,
self.valueInDollars,
self.dateCreated ];
return descriptionString;
}
#pragma mark - NSCoding
- (void) encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject :self.itemName forKey :@"itemName"];
[aCoder encodeObject :self.serialNumber forKey :@"serialNumber"];
[aCoder encodeObject :self.dateCreated forKey :@"dateCreated"];
[aCoder encodeInt :self.valueInDollars forKey:@"valueInDollars"];
}
- (instancetype) initWithCoder:(NSCoder *)aDecoder {
self = [super init];
if (self) {
_itemName = [aDecoder decodeObjectForKey :@"itemName"];
_serialNumber = [aDecoder decodeObjectForKey :@"serialNumber"];
_dateCreated = [aDecoder decodeObjectForKey :@"dateCreated"];
_valueInDollars = [aDecoder decodeIntForKey :@"valueInDollars"];
}
return self;
}
@end
UITableView 數(shù)據(jù)源
- 在 Cocoa Touch 中,
UITableView
對象會自己查詢另一個對象以獲得需要顯示的內(nèi)容,這個對象就是UITableView
對象的數(shù)據(jù)源,也就是dataSource
屬性所指向的對象。 - 該應(yīng)用中,
UITableView
對象的數(shù)據(jù)源就是HQLItemsViewController
對象自己。所以要為HQLItemsViewController
對象添加相應(yīng)的屬性和方法,使其能夠保存多個HQLItem
對象。 - 使用
HQLItemStore
(類型為NSMutableArray
)對象來負(fù)責(zé)保存和加載HQLItem
對象,當(dāng)某個對象需要訪問所有的HQLItem
時,可以通過HQLItemStore
的allItems
方法獲取包含所有HQLItem
的NSMutableArray
。此外,HQLItemStore
還會負(fù)責(zé)將HQLItem
存入文件,或者從文件重新載入。
5.創(chuàng)建 HQLItemStore
-
HQLItemStore
對象是一個單例對象。也就是說,每個應(yīng)用只會有一個這種類型的對象。如果應(yīng)用嘗試創(chuàng)建另一個對象,HQLItemStore
類就會返回已經(jīng)存在的那個對象。
#import <Foundation/Foundation.h>
@interface HQLItemStore : NSObject
//將此類設(shè)置為單例對象
+ (instancetype)sharedStore;
@end
- 在
HQLItemStore.m
中實現(xiàn)sharedStore
單例方法,同時編寫一個拋出異常的init
方法和私有指定初始化方法initPrivate
。
@implementation HQLItemStore
+ (instancetype)sharedStore {
//將sharedStore聲明為了靜態(tài)變量,當(dāng)某個定義了靜態(tài)變量的方法返回時,程序不會釋放相應(yīng)的變量
static HQLItemStore *sharedStore = nil;
//判斷是否需要創(chuàng)建一個sharedStore對象
// (! sharedStore) 為真 ,即(sharedStore)為假,不存在
if (! sharedStore) {
sharedStore = [[self alloc] initPrivate];
}
return sharedStore;
}
// 如果誤調(diào)用了 [[HQLItemstore alloc] init],就提示應(yīng)該使用 [HQLItemstore sharedStore]。
- (instancetype)init {
@throw [NSException exceptionWithName:@"Singleton"
reason:@"Use + [HQLItemStore sharedStore]"
userInfo:nil];
return nil;
}
// 這是真正的(私有的)初始化方法
- (instancetype)initPrivate {
self = [super init];
return self;
}
- 在
HQLItemStore.h
中聲明一個方法和一個屬性,分別用于創(chuàng)建和保存HQLItem
對象。
#import <Foundation/Foundation.h>
//@class 只需要使用類的聲明,無需知道具體的實現(xiàn)細(xì)節(jié)
@class HQLItem;
@interface HQLItemStore : NSObject
//保存 HQLItem
//allItems屬性被聲明為NSArray(不可變數(shù)組),且設(shè)置為readonly,這樣其他類既無法將一個新的數(shù)組賦給allItems,也無法修改allItems
//allItems屬性對外部公開使用
@property (nonatomic, readonly) NSArray *allItems;
+ (instancetype)sharedStore;
//創(chuàng)建 HQLItem
- (HQLItem *)createItem;
@end
- 在
HQLItemStore.m
頂部導(dǎo)入HQLItem.h
文件,以便之后向HQLItem.h
對象發(fā)送消息。 - 接下來在
HQLItemStore.m
的類擴(kuò)展中聲明一個可變數(shù)組。
#import "HQLItemStore.h"
#import "HQLItem.h"
@interface HQLItemStore ()
// 類擴(kuò)展中為NSMutableArray(可變數(shù)組)
//privateItems屬性只在內(nèi)部使用
@property (nonatomic) NSMutableArray *privateItems;
@end
@implementation HQLItemStore
...
- 在
HQLItemStore.m
中實現(xiàn)initPrivate
方法,初始化privateItem
屬性。同時還需要覆蓋allItem
的取方法,返回privateItems
,同時實現(xiàn)createItem
方法。
- (instancetype)initPrivate {
self = [super init];
//父類的init方法是否成功創(chuàng)建了對象
if (self) {
_privateItems = [[NSMutableArray alloc] init];
}
return self;
}
- (Item *)createItem {
HQLItem *item = [HQLItem randomItem];
[self.privateItems addObject:item];
return item;
}
//allItems取方法,返回值為NSArray類型
- (NSArray *)allItems {
//方法體中返回值為NSMutableArray類型
return self.privateItems;
}
6.實現(xiàn)數(shù)據(jù)源方法
在 HQLItemsViewController.m
頂部導(dǎo)入 HQLItemStore.h
和 HQLItem.h
,然后更新指定初始化方法,創(chuàng)建 5 個隨機(jī)的 HQLItem 對象并加入 HQLItemStore對象。
-(instancetype) init {
//調(diào)用父類的指定初始化方法
self = [super initWithStyle:UITableViewStyleGrouped];
//初始化生成隨機(jī)對象
if (self) {
for (int i = 0; i < 5; i ++) {
[[HQLItemStore sharedStore] createItem];
}
}
return self;
}
在 QLItemViewController.m
中實現(xiàn)數(shù)據(jù)源協(xié)議 tableView: numberOfRowsInSection:
方法
//返回應(yīng)該顯示的行數(shù)
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [[[HQLItemStore sharedStore] allItems] count];
}
另一個必須要實現(xiàn)的數(shù)據(jù)源協(xié)議是 tableView: cellForRowAtIndexPath:
注:實現(xiàn)該方法還涉及到另一個類:UITableViewCell
,此類的詳解及創(chuàng)建自定義子類日后分析。
//獲取用于顯示第section個表格段、第row行數(shù)據(jù)的UITableViewCell對象
//返回各行所需視圖,每個表格段對應(yīng)一組獨立的行
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"UITableViewCell"];
//由協(xié)議獲取已經(jīng)分配的單元格,而不是分配一個新的,
//創(chuàng)建或重用UITableViewCell對象
//按照約定,應(yīng)該將UITableViewCell或者UITableViewCell子類的類名用作reuseIdentifier。
UITableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:@"UITableViewCell"
forIndexPath:indexPath];
//獲取allItem的第n個 HQLItem 對象,這里的n是該UITableViewCell對象所對應(yīng)的表格行索引
//然后將該Item對象的描述信息賦給UITableViewCell對象的textlabel
NSArray *items = [[HQLItemStore sharedStore] allItems];
HQLItem *item = items[indexPath.row];
cell.textLabel.text = [item description];
return cell;
}
7. 重用 UITableViewCell 對象
UITableView
對象會將移出窗口的 UITableViewCell
對象放入UITableViewCell
對象池,等待重用。當(dāng) UITableView
對象要求數(shù)據(jù)源返回某個 UITableViewCell
對象時,數(shù)據(jù)源可以先查看對象池。如果有未使用的UITableViewCell
對象,就可以用新的數(shù)據(jù)配置這個 UITableViewCell
對象,然后將其返回給 UITableView
對象,從而避免創(chuàng)建新對象。同時,為了重用 UITableViewCell
對象,需要將創(chuàng)建UITableViewCell
對象的過程交由系統(tǒng)管理,如果對象池中沒有 UITableViewCell
對象,則由系統(tǒng)初始化創(chuàng)建所需類型的 UITableViewCell
對象。
- (void)viewDidLoad {
[super viewDidLoad];
// 重用 UITableViewCell,向表視圖注冊應(yīng)該使用的 UITableViewCell 類型
[self.tableView registerClass:[UITableViewCell class]
forCellReuseIdentifier:@"UITableViewCell"];
}
8. 編輯模式
在編輯模式下,用戶可以管理 UITableView
中的表格行,例如添加、刪除和移動等操作。
為應(yīng)用添加編輯模式的界面有兩種方式:
1?? 在視圖控制器頂層添加 NavigationController
;
2?? 為 UITableView
對象添加表頭視圖。
方法一:在視圖控制器頂層添加導(dǎo)航視圖控制器
- 將導(dǎo)航視圖控制器設(shè)置為根視圖控制器
AppDelegate.m:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// 創(chuàng)建 HQLItemsViewController 對象
HQLItemsViewController *itemsViewController = [[HQLItemsViewController alloc] init];
// 創(chuàng)建 UINavigationController 對象
// 將 HQLItemsViewController 對象設(shè)置為 UINavigationController 對象的根視圖控制器
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:itemsViewController];
// 將 UINavigationController 對象設(shè)置為 UIWindow 對象的根視圖控制器
self.window.rootViewController = navigationController;
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}
- 在 HQLItemsViewController.m 中設(shè)置導(dǎo)航欄標(biāo)題和按鈕
-(instancetype) init {
// 調(diào)用父類的指定初始化方法
self = [super initWithStyle:UITableViewStylePlain];
// 初始化生成隨機(jī)對象
if (self) {
// 設(shè)置導(dǎo)航欄標(biāo)題
UINavigationItem *navItem = self.navigationItem;
navItem.title = @"Homepwner";
// 為導(dǎo)航欄設(shè)置【添加】和【編輯】按鈕,以替換表頭視圖(headerView)
// 創(chuàng)建新的 UIBarButtonItem 對象
// 將其目標(biāo)對象設(shè)置為當(dāng)前對象,將其動作方法設(shè)置為 addNewItem
UIBarButtonItem *bbi = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemAdd
target:self
action:@selector(addNewItem:)];
// 為 UINavigationItem 對象的 rightBarButtonItem 屬性賦值,
// 指向新創(chuàng)建的 UIBarButtonItem 對象
navItem.rightBarButtonItem = bbi;
// 為 UINavigationBar 對象添加編輯按鈕
navItem.leftBarButtonItem = self.editButtonItem;
}
return self;
}
方法二:為 UITableView
對象添加表頭視圖
- 創(chuàng)建一個針對表格的表頭視圖
創(chuàng)建一個新的 XIB 文件。cmd+N -> 在 User Interface 窗口中選擇 Empty,將文件名設(shè)置為 HeaderView.xib
并保存。打開 Interface Builder 后,先拖拽一個UIView
對象至畫布,再添加兩個 UIButton
對象。
?? 注意一定要選擇 Empty 類別的XIB文件,有一次我錯選了 View,編譯運(yùn)行測試就是加載不出視圖來,老糾結(jié)了??
選中File's Owner,修改Class文本框為 HQLItemsViewController
。
接著選中 UIView
對象,將 Size
屬性設(shè)置為 Freeform
以調(diào)整視圖對象大小 。
將 UIView
對象的背景顏色改為全透明顏色,即 ClearColor
.
- 在
HQLItemViewController.m
的類擴(kuò)展中聲明插座變量headerView
,并添加兩個動作方法。
@interface HQLItemsViewController ()
// 載入XIB文件后,headerView會指向XIB文件中的頂層對象,并且是強(qiáng)引用。
// ?? 指向頂層對象的插座變量必須聲明為強(qiáng)引用;相反,當(dāng)插座變量指向頂層對象所擁有的對象(例如頂層對象的子視圖時),應(yīng)該使用弱引用。
@property (nonatomic,strong) IBOutlet UIView *headerView;
@end
@implementation HQLItemsViewController
// ...
#pragma mark 表頭視圖按鈕
// 添加新項目
- (IBAction)addNewItem:(id)sender {
// 創(chuàng)建新的 Item 對象并將其加入 HQLItemStore 對象
Item *newItem = [[HQLItemStore sharedStore] createItem];
// 獲取新創(chuàng)建的對象在 allItem 數(shù)組中的索引
NSInteger lastRow = [[[HQLItemStore sharedStore] allItems] indexOfObject:newItem];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:lastRow
inSection:0];
// 將新行插入UITableview對象
[self.tableView insertRowsAtIndexPaths:@[indexPath]
withRowAnimation:UITableViewRowAnimationTop];
}
// 切換編輯模式
- (IBAction)toggleEditingMode:(id)sender {
// 如果當(dāng)前的視圖控制對象已經(jīng)處在編輯模式
if (self.isEditing) {
// 修改按鈕文字,提示用戶當(dāng)前的表格狀態(tài)
[sender setTitle:@"Edit" forState:UIControlStateNormal];
// 關(guān)閉編輯模式
[self setEditing:NO animated:YES];
}else {
// 修改按鈕文字,提示用戶當(dāng)前的表格狀態(tài)
[sender setTitle:@"Done" forState:UIControlStateNormal];
// 開啟編輯模式
[self setEditing:YES animated:YES];
}
}
- 在
HQLItemsViewController.m
中使用 Lazy Loading 方式實現(xiàn)hearerView
的getter
方法,載入應(yīng)用程序包中的 XIB 文件。
// 載入headerView.xib文件
- (UIView *)headerView {
// 如果還沒有載入headerView
if (!_headerView) {
/* 載入指定的XIB文件
*
* 將 self 作為 owner 實參(擁有者)傳給 NSBundle 對象,
* 目的是當(dāng) HQLItemsViewController 將XIB文件加載為NIB文件時,
* 使用 HQLItemsViewController 對象自身替換占位符對象 File's Owner
*
*/
[[NSBundle mainBundle] loadNibNamed:@"HeaderView"
owner:self
options:nil];
}
return _headerView;
}
- 將 headerView 設(shè)置為
UITableView
對象的表頭視圖。在HQLItemsViewController.m
的ViewDidLoad
方法中添加以下代碼:
// 加載headerView,并將其設(shè)置為UITableView對象的表頭視圖
UIView *header = self.headerView;
[self.tableView setTableHeaderView:header];
9. 增加行
該應(yīng)用中,增加行的實現(xiàn)方式是,在表視圖上放置添加按鈕,點擊添加按鈕之后系統(tǒng)調(diào)用 createItem
方法創(chuàng)建隨機(jī)對象.
10. 刪除行
如果 UITableView
對象請求確認(rèn)的是刪除操作,刪除 Homepwner 中的某個表格行(即 UITableViewCell
對象)步驟:
- 刪除視圖。從
UITableView
對象刪除指定的UITableViewCell
對象; - 刪除模型。找到和需要刪除的
UITableViewCell
對象對應(yīng)的HQLItem
對象,也將其從HQLItemStore
中刪除。
?
完成第 2 步需要在HQLItemStore.h
中增加一個刪除方法removeItem
,用于移除指定的HQLItem
對象,接著在HQLItemStore.m
文件中實現(xiàn)該方法。
NSMutableArray
中的刪除方法:
-
removeItem
方法調(diào)用了NSMutableArray
中的removeObjectIdenticalTo:
比較指向?qū)ο蟮闹羔槪摲椒ㄖ粫瞥龜?shù)組所保存的那些和傳入對象指針完全相同的指針。 -
removeObject:
該方法會枚舉數(shù)組,向每一個對象發(fā)送isEqual:
消息,判斷當(dāng)前對象和傳入對象所包含的數(shù)據(jù)是否相等。
- (void)removeItem:(Item *)item {
[self.privateItems removeObjectIdenticalTo:item];
}
接下來為 HQLItemViewController
實現(xiàn)方法 tableView:commitEditingStyle:forRowAtIndexPath:
- (void)tableView:(UITableView *)tableView //發(fā)送該消息的UITableView對象
commitEditingStyle:(UITableViewCellEditingStyle)editingStyle //編輯風(fēng)格
forRowAtIndexPath:(NSIndexPath *)indexPath { //相應(yīng)表格行所在的表格段索引和行索引
//如果UITableView對象請求確認(rèn)的是刪除操作
if (editingStyle ==UITableViewCellEditingStyleDelete) {
//先刪除Item對象
NSArray *items = [[HQLItemStore sharedStore] allItems];
Item *deleteItem = items [indexPath.row];
[[HQLItemStore sharedStore] removeItem:deleteItem];
//還要刪除表格視圖中的相應(yīng)表格行(帶動畫效果)
[tableView deleteRowsAtIndexPaths:@[indexPath]
withRowAnimation:UITableViewRowAnimationFade];
}
}
11. 更改刪除按鈕的標(biāo)題文本
刪除 UITableView
對象中的某個表格行時,相應(yīng)的 UITableViewCell
對象會在其右側(cè)顯示一個標(biāo)題為“Delete”的按鈕,先將該按鈕標(biāo)題改為中文“刪除”。
- (NSString *)tableView:(UITableView *)tableView
titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath {
return @"刪除";
}
12. 移動行
要改變 UITableView
對象所顯示的行的排列位置,需要為數(shù)據(jù)源實現(xiàn)另一個UITableViewDataSource
協(xié)議的方法,
首先要為數(shù)據(jù)源實現(xiàn)移動方法: moveItemAtIndex:toIndex:
,為 HQLItemStore
增加該新方法,同樣需要先在 .h 文件中聲明,然后在 .m 文件中實現(xiàn)。
- (void)moveItemAtIndex:(NSUInteger)fromIndex toIndex:(NSUInteger)toIndex {
if (fromIndex == toIndex) {
return;
}
//得到要移動的對象的指針,以便稍后能將其插入新的位置
Item *item = self.privateItems [fromIndex];
//將item從allItem數(shù)組所在位置中移除
[self.privateItems removeObjectAtIndex:fromIndex];
//根據(jù)新的索引的位置,將item重新插回allItem數(shù)組新的位置
[self.privateItems insertObject:item atIndex:toIndex];
}
接下來在 HQLItemViewController.m
中實現(xiàn)tableView:moveRowAtIndexPath:toIndexPath:
,更新 HQLItemStore
對象。
- (void)tableView:(UITableView *)tableView
moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath
toIndexPath:(NSIndexPath *)destinationIndexPath {
[[HQLItemStore sharedStore]moveItemAtIndex:sourceIndexPath.row toIndex:destinationIndexPath.row];
}
(二)在代碼中使用自動布局
將之前添加到 Interface Builder 中的 ImageView 刪除,改用代碼方式創(chuàng)建,并使用視覺格式化語言 VFL 為其自動布局:
- 通常,如果是創(chuàng)建整個視圖層次結(jié)構(gòu)及所有視圖約束,就覆蓋
loadView
方法; - 如果只是向通過 NIB 文件創(chuàng)建的視圖層次結(jié)構(gòu)中添加一個視圖或約束,就覆蓋
viewDidLoad
方法。
- (void)viewDidLoad {
[super viewDidLoad];
// ------------------------
// 在代碼中使用自動布局 VFL 視覺化格式語言
// 創(chuàng)建 UIImageView 對象
UIImageView *iv = [[UIImageView alloc] initWithImage:nil];
// 設(shè)置 UIImageView 對象的內(nèi)容縮放模式
iv.contentMode = UIViewContentModeScaleAspectFit;
// 在 Apple 引入自動布局系統(tǒng)之前,iOS 一直使用自動縮放掩碼(autoresizing masks)縮放視圖,以適配不同大小的屏幕。
// 默認(rèn)情況下,視圖會將自動縮放掩碼轉(zhuǎn)換為對應(yīng)的約束,這類約束經(jīng)常會與手動添加的約束產(chǎn)生沖突。
// 告訴自動布局系統(tǒng)不要將自動縮放掩碼轉(zhuǎn)換為約束
iv.translatesAutoresizingMaskIntoConstraints = NO;
// 將 UIImageView 對象添加到 view 上
[self.view addSubview:iv];
// 將 UIImageView 對象賦給 imageView 屬性
self.imageView = iv;
// 初始 UITextField 的內(nèi)容放大優(yōu)先級是 250,而 imageView 的內(nèi)容放大優(yōu)先級是 251
// 如果用戶選擇了一張小尺寸圖片,自動布局系統(tǒng)會增加 UITextField 對象的高度,使得高度超出 UITextField 對象的固有內(nèi)容大小
// 將 imageView 垂直方向的優(yōu)先級設(shè)置為比其他視圖低的數(shù)值
// 設(shè)置垂直方向上的【內(nèi)容放大優(yōu)先級】
[self.imageView setContentHuggingPriority:200
forAxis:UILayoutConstraintAxisVertical];
// 設(shè)置垂直方向上的【內(nèi)容縮小優(yōu)先級】
[self.imageView setContentCompressionResistancePriority:700
forAxis:UILayoutConstraintAxisVertical];
// 創(chuàng)建視圖名稱字典,將名稱與視圖對象關(guān)聯(lián)起來
NSDictionary *nameMap = @{
@"imageView" :self.imageView,
@"dateLabel" :self.dateLabel,
@"toolbar" :self.toolbar
};
// imageView 的左邊和右邊與父視圖的距離都是0點
NSArray *horizontalConstraints =
[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[imageView]-0-|"
options:0
metrics:nil
views:nameMap];
// imageView 的頂邊與 dateLabel 的距離是8點,底邊與 toolbar 的距離也是8點
NSArray *verticalConstrants = [NSLayoutConstraint
constraintsWithVisualFormat:@"V:[dateLabel]-[imageView]-[toolbar]"
options:0
metrics:nil
views:nameMap];
// 將兩個 NSLayoutConstraint 對象數(shù)組添加到 HQLDetailViewControl 的 view 中
[self.view addConstraints:horizontalConstraints];
[self.view addConstraints:verticalConstrants];
}
視覺化格式語言(visual formart language,VFL)
- 視覺化格式語言 定義了一系列使用字符串描述約束的象形語法,而這類字符串稱為視覺化格式字符串。
- 視覺化格式字符串可以描述一個方向上的多個約束。
字符 | 含義 |
---|---|
H | 水平方向(horizontal) |
V | 垂直方向 (vertical) |
[] | 視圖需要寫在方括號[ ]中 |
| | 表示父視圖 |
-10- | 約束距離為10 |
[someView] (==50) | 限定某個視圖的寬或者高為50 |
- 描述水平間距的視覺化格式字符串:
@"H:|-0-[imageView]-0-|"
含義:imageView 的左邊和右邊與父視圖的距離都是0點。
在視覺化格式語言中,0及其連接符可以省略不寫,即
@"H:|[imageView]|"
更復(fù)雜的約束:
@"H:|-20-[imageView1]-10-[imageView2]-20-|"
- 垂直方向上:
- 在垂直方向上,字符串的左邊表示頂邊,右邊表示底邊。
@"V:[dateLabel]-[imageView]-[toolbar]"
含義:mageView 的頂邊與 dateLabel 的距離是8點,底邊與 toolbar 的距離也是8點。
@"V:[someView (==50)]"
含義:限定某個視圖的寬或者高為50
為了讓自動布局系統(tǒng)知道視覺格式化字符串中的名稱所表示的視圖對象,需要通過視圖名稱字典將名稱與視圖對象關(guān)聯(lián)起來。
// 創(chuàng)建視圖名稱字典,將名稱與視圖對象關(guān)聯(lián)起來
NSDictionary *nameMap = @{
@"imageView" :self.imageView,
@"dateLabel" :self.dateLabel,
@"toolbar" :self.toolbar
};
如何判斷約束應(yīng)該添加到哪個視圖中?
- 如果約束同時對【多個父視圖相同的視圖】起作用,那么約束應(yīng)該添加到它們的父視圖中。
- 如果約束只對【某個視圖自身】起作用,那么約束應(yīng)該添加到該視圖中。
- 如果約束同時對【多個父視圖不同的視圖】起作用,但是這些視圖在層次結(jié)構(gòu)中有共同的祖先視圖,那么約束應(yīng)該添加到它們最近一級的祖先視圖中。
- 如果約束同時對【某個視圖及其父視圖】起作用,那么約束應(yīng)該添加到它們的父視圖中。
// 將兩個 NSLayoutConstraint 對象數(shù)組添加到 HQLDetailViewControl 的 view 中
[self.view addConstraints:horizontalConstraints];
[self.view addConstraints:verticalConstrants];
NSLayoutConstraint
// view1.attr1 relation view2.attr2 * multiplier + c
+ (instancetype)constraintWithItem:(id)view1
attribute:(NSLayoutAttribute)attr1
relatedBy:(NSLayoutRelation)relation
toItem:(id)view2
attribute:(NSLayoutAttribute)attr2
multiplier:(CGFloat)multiplier
constant:(CGFloat)c;
(三)自動轉(zhuǎn)屏,UIPopoverController 與模態(tài)視圖控制器
自動轉(zhuǎn)屏
設(shè)備類型
物理設(shè)備類型
typedef enum UIUserInterfaceIdiom : NSInteger {
UIUserInterfaceIdiomUnspecified = -1,
UIUserInterfaceIdiomPhone,
UIUserInterfaceIdiomPad,
UIUserInterfaceIdiomTV,
UIUserInterfaceIdiomCarPlay
} UIUserInterfaceIdiom;
設(shè)備方向(device orientation)
設(shè)備方向指的是設(shè)備的物理方向
typedef enum UIDeviceOrientation : NSInteger {
UIDeviceOrientationUnknown, // 未知方向
UIDeviceOrientationPortrait, // 正的豎排方向
UIDeviceOrientationPortraitUpsideDown, // 倒置方向
UIDeviceOrientationLandscapeLeft, // 左旋轉(zhuǎn)方向
UIDeviceOrientationLandscapeRight, // 右旋轉(zhuǎn)方向
UIDeviceOrientationFaceUp, // 正面朝上
UIDeviceOrientationFaceDown // 正面朝下
} UIDeviceOrientation;
界面方向(interface orientation)
界面方向指的是用戶所看到的應(yīng)用界面的方向。
typedef enum UIInterfaceOrientation : NSInteger {
// 未知方向
UIInterfaceOrientationUnknown = UIDeviceOrientationUnknown,
// 豎排方向,主屏幕按鈕位于屏幕下方
UIInterfaceOrientationPortrait = UIDeviceOrientationPortrait,
// 豎排方向,主屏幕按鈕位于屏幕上方
UIInterfaceOrientationPortraitUpsideDown = UIDeviceOrientationPortraitUpsideDown,
// 橫排方向,主屏幕按鈕位于屏幕右側(cè)
UIInterfaceOrientationLandscapeLeft = UIDeviceOrientationLandscapeRight,
// 橫排方向,主屏幕按鈕位于屏幕左側(cè)
UIInterfaceOrientationLandscapeRight = UIDeviceOrientationLandscapeLeft
} UIInterfaceOrientation;
自動轉(zhuǎn)屏通告機(jī)制
// 視圖即將顯示時調(diào)用
- (void)viewWillAppear:(BOOL)animated {
// 添加自動轉(zhuǎn)屏通知:iPhone 橫屏狀態(tài)下禁用拍照按鈕
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(deviceOrientationDidChange:)
name:UIDeviceOrientationDidChangeNotification
object:nil];
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
}
// 視圖即將出棧時調(diào)用
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter]
removeObserver:self
name:UIDeviceOrientationDidChangeNotification
object:nil];
}
- (void)deviceOrientationDidChange:(NSNotification *)notification {
UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
[self prepareViewsForOrientation:orientation];
}
- (void)prepareViewsForOrientation:(UIInterfaceOrientation)orientation {
// 如果是 iPad,則不執(zhí)行任何操作
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
return;
}
// 判斷設(shè)備是否處于橫屏方向
if (UIInterfaceOrientationIsLandscape(orientation)) {
self.imageView.hidden = YES;
self.cameraButton.enabled = NO;
} else {
self.imageView.hidden = NO;
self.cameraButton.enabled = YES;
}
}
UIPopoverController [Deprecated]
類型為 Block 的 completion 實參
之前添加的新項目是直接插入列表中顯示
// 添加新項目
- (IBAction)addNewItem:(id)sender {
// 創(chuàng)建新的 Item 對象并將其加入 HQLItemStore 對象
Item *newItem = [[HQLItemStore sharedStore] createItem];
// 獲取新創(chuàng)建的對象在 allItem 數(shù)組中的索引
NSInteger lastRow = [[[HQLItemStore sharedStore] allItems] indexOfObject:newItem];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:lastRow
inSection:0];
// 將新行插入UITableview對象
[self.tableView insertRowsAtIndexPaths:@[indexPath]
withRowAnimation:UITableViewRowAnimationTop];
}
現(xiàn)在點擊 ”+” 按鈕添加新項目 ? 把新項目以模態(tài)視圖的方式顯示在 HQLDetailViewControl 對象中;
如果選擇 Cancel 取消,則刪除剛剛創(chuàng)建的新項目。
如果選擇 Done 完成,則添加新項目到列表中,返回的時候還要刷新列表。
實現(xiàn)步驟:
1. HQLDetailViewControl.h 中添加一個 Block 屬性。
@property (nonatomic, copy) void(^dismissBlock)(void);
2. 添加新項目時,把新創(chuàng)建的 HQLDetailViewControl 對象以新創(chuàng)建的 UINavigationController 的根視圖控制器模態(tài)呈現(xiàn)
// 添加新項目
- (IBAction)addNewItem:(id)sender {
// 創(chuàng)建新的 Item 對象并將其加入 HQLItemStore 對象
Item *newItem = [[HQLItemStore sharedStore] createItem];
// 把新項目以模態(tài)視圖的方式顯示在 HQLDetailViewControl 對象中
HQLDetailViewControl *detailViewController = [[HQLDetailViewControl alloc] initForNewItem:YES];
detailViewController.item = newItem;
// ?? Block 的代碼塊
detailViewController.dismissBlock = ^{
[self.tableView reloadData];
};
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:detailViewController];
// 修改視圖控制器的模態(tài)樣式(對于 iPad 有效):頁單樣式
navController.modalPresentationStyle = UIModalPresentationFormSheet;
[self presentViewController:navController animated:YES completion:nil];
}
3. 修改指定初始化方法
- (instancetype)initForNewItem:(BOOL)isNew {
self = [super initWithNibName:nil bundle:nil];
if (self) {
if (isNew) {
// 導(dǎo)航欄完成按鈕
UIBarButtonItem *doneItem = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemDone
target:self
action:@selector(save:)];
self.navigationItem.rightBarButtonItem = doneItem;
// 導(dǎo)航欄取消按鈕
UIBarButtonItem *cancelItem = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemCancel
target:self
action:@selector(cancel:)];
self.navigationItem.leftBarButtonItem = cancelItem;
}
}
return self;
}
- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
@throw [NSException exceptionWithName:@"Wrong initializer" reason:@"Use initForNewItem" userInfo:nil];
return nil;
}
4. 實現(xiàn)導(dǎo)航欄按鈕,模態(tài)退出時傳入 Block 對象
- (void)save:(id)sender {
// UIViewController 對象的 presentingViewController 屬性:
// 當(dāng)【某個 UIViewController 對象】以模態(tài)形式顯示時,該屬性會指向【~~顯示該對象的那個 UIViewController 對象~~】(包含該對象的 UINavigationController 對象)
// 所以下面一行代碼的意思是 向 HQLItemsViewController 對象發(fā)送關(guān)閉模態(tài)視圖消息
[self.presentingViewController dismissViewControllerAnimated:YES
completion:self.dismissBlock];
}
- (void)cancel:(id)sender {
// 如果用戶按下了 Cancel 按鈕,就從 HQLItemStore 對象移除新創(chuàng)建的 Item 對象
[[HQLItemStore sharedStore] removeItem:self.item];
[self.presentingViewController dismissViewControllerAnimated:YES
completion:self.dismissBlock];
}
以模態(tài)形式顯示視圖控制器時的動畫效果
typedef enum UIModalTransitionStyle : NSInteger {
UIModalTransitionStyleCoverVertical = 0, // 默認(rèn),從底部滑入
UIModalTransitionStyleFlipHorizontal, // 以3D 效果翻轉(zhuǎn)
UIModalTransitionStyleCrossDissolve, // 淡入
UIModalTransitionStylePartialCurl // 模擬書頁卷角
} UIModalTransitionStyle;
示例:
detailViewController.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
線程安全的單例
在同一時間,單線程應(yīng)用只能使用 CPU 的一個核,也只能執(zhí)行一個函數(shù)。相反,多線程應(yīng)用可以同時在不同的 CPU 核上執(zhí)行多個函數(shù)。
單線程應(yīng)用中創(chuàng)建單例
以 HQLImageStore 類為例:
#pragma 單例類
+ (instancetype)sharedStore{
static HQLImageStore *sharedStore;
if (!sharedStore) {
sharedStore = [[self alloc] initPrivate];
}
return sharedStore;
}
// 私有化方法
- (instancetype) initPrivate{
self = [super init];
if (self) {
_dictionary = [[NSMutableDictionary alloc] init];
}
return self;
}
// 不允許直接調(diào)用init方法
- (instancetype) init{
@throw [NSException exceptionWithName:@"Singleton"
reason:@"Use + [HqlImageStore sharedStored]"
userInfo:nil];
return nil;
}
以上代碼在單線程應(yīng)用中可以正確創(chuàng)建單例,但是在多線程應(yīng)用中,以上代碼可能會創(chuàng)建多個 HQLImageStore
對象。同時,某個線程還可能會訪問其他線程中沒有正確初始化的 HQLImageStore
對象。
使用 dispatch_once ()
+ (instancetype)sharedInstance
{
static id sharedInstance = nil;
static dispatch_once_t onceToken = 0;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
有可用的代碼塊:??
視圖控制器之間的關(guān)系
- ① 父—子關(guān)系;
- ② 顯示—被顯示關(guān)系;
① 父—子關(guān)系
- 當(dāng)使用 視圖控制器容器(view controller container)時,就會產(chǎn)生擁有父—子關(guān)系的視圖控制器。
-
UINavigationController
、UITabBarController
和UISplitViewController
都是視圖控制器容器。 - 容器對象會將
viewControllers
中的視圖作為子視圖加入自己的視圖。 - 容器對象通常都有自己的默認(rèn)外觀。
- 處在同一個父—子關(guān)系下的視圖控制器形成一個族系(family)。
對象相互訪問:
- 任何容器對象都可以通過
viewControllers
訪問其子對象。 - 子對象可以通過
UIViewController
對象的四個特定屬性來訪問其容器對象:navinavigationController
tabBarController
splitViewController
-
parentViewController
,該屬性會指向族系中”最近”的那個容器對象。
② 顯示—被顯示關(guān)系;
當(dāng)某個視圖控制器以模態(tài)形式顯示另一個視圖控制器時,就會產(chǎn)生擁有顯示—被顯示關(guān)系的視圖控制器。
在顯示—被顯示關(guān)系中,位于關(guān)系兩頭的視圖控制器不會處于同一個族系中。被顯示的視圖控制器會有自己的族系。
當(dāng)應(yīng)用以模態(tài)形式顯示某個視圖控制器時,負(fù)責(zé)顯示該視圖控制器的將是相關(guān)族系中的頂部視圖控制器。
如果設(shè)置
definesPresentationContext
屬性為 YES ,那么該視圖控制器就會自己負(fù)責(zé)顯示新的視圖控制器,而不是將“顯示權(quán)”向上傳遞。
對象相互訪問
視圖控制器 A——>模態(tài)形式顯示視圖控制器 B:
- A.
presentedViewController
= B - B.
presentingViewController
= A
保存、讀取與應(yīng)用狀態(tài)
略.
Auto Layout 中的兩個屬性
- Content Hugging Priority: 內(nèi)容放大優(yōu)先級
- Content Compression Resistance Priority:內(nèi)容縮小優(yōu)先級
所有視圖都具有 intrinsicContentSize
屬性,表示視圖的固有內(nèi)容大小,自動布局系統(tǒng)會根據(jù)固有內(nèi)容大小自動為視圖添加寬度和高度約束。
如果需要讓自動布局系統(tǒng)在必要時基于固有內(nèi)容大小放大視圖尺寸,則可以為視圖添加一個優(yōu)先級比視圖的內(nèi)容放大優(yōu)先級(Content Hugging Priority)高的約束;
相反,如果需要讓自動布局系統(tǒng)在必要時基于固有內(nèi)容大小縮小視圖尺寸,則可以為視圖添加一個優(yōu)先級比視圖的內(nèi)容縮小優(yōu)先級(Content Compression Resistance Priority)高的約束;
高優(yōu)先級的的視圖會保持固有內(nèi)容大小,低優(yōu)先級的視圖會根據(jù)當(dāng)前約束拉伸或縮小該視圖的高度或?qū)挾取?/p>