UI狀態(tài)保存和恢復(fù)(三)
前面兩篇我們介紹了UI狀態(tài)保存和恢復(fù)的流程哨坪,UIStateRestoration協(xié)議類(lèi)的方法,適用場(chǎng)景乍楚,調(diào)試策略当编,UIApplication,UIViewController,UIView關(guān)于UIStateRestoration協(xié)議所提供的接口方法以及如何實(shí)現(xiàn)UI狀態(tài)保存和恢復(fù)。本篇我們將介紹UIStateRestoration協(xié)議類(lèi)中的UIDataSourceModelAssociation
協(xié)議徒溪。
關(guān)于UIDataSourceModelAssociation
協(xié)議
引用官網(wǎng)的解釋
Your data source objects can adopt this protocol to assist a corresponding table or collection view during the state restoration process. Those classes use the methods of this protocol to ensure that the same data objects (and not just the same row indexes) are scrolled into view and selected.
//你的數(shù)據(jù)源對(duì)象可以實(shí)現(xiàn)這個(gè)協(xié)議忿偷,在狀態(tài)恢復(fù)的過(guò)程中去支持相關(guān)的table or collection view;這些實(shí)現(xiàn)了該協(xié)議的類(lèi)臊泌,使用這個(gè)協(xié)議的方法去保證相同的數(shù)據(jù)對(duì)象鲤桥,(而不僅僅是相同的行的索引)被滾動(dòng)到視圖并且被選中。
Before you can implement this protocol, your app must be able to identify data objects consistently between app launches. This requires being able to take some identifying marker of the object and convert that marker into a string that can then be saved with the rest of the app state. For example, a Core Data app could convert a managed object’s ID into a URI that it could then convert into a string.
//在你實(shí)現(xiàn)這個(gè)協(xié)議之前渠概,你的App必須能夠在App啟動(dòng)之間茶凳,一直(總是可以)辨別出數(shù)據(jù)源對(duì)象。這就要求對(duì)象能夠有一些辨認(rèn)標(biāo)識(shí)高氮,并且可以把標(biāo)識(shí)轉(zhuǎn)換為當(dāng)App狀態(tài)不活躍時(shí)能夠被存儲(chǔ)的字符串慧妄;
Currently, only the UITableView and UICollectionView classes support this protocol. You would implement this protocol in any objects you use as the data source for those classes. If you do not adopt the protocol in your data source, the views do not attempt to restore the selected and visible rows.
//目前,只有 UITableView 和 UICollectionView 類(lèi) 支持這個(gè)協(xié)議剪芍。你將可以實(shí)現(xiàn)這個(gè)協(xié)議在任何你用來(lái)作為UITableView 和 UICollectionView數(shù)據(jù)源的對(duì)象中塞淹,如果在你的數(shù)據(jù)源對(duì)象中不實(shí)現(xiàn)這個(gè)協(xié)議,那么視圖將不會(huì)試著去恢復(fù)選中的和可見(jiàn)rows;
我們可以獲取到的主要信息有:
- 只有
UITableView
和UICollectionView
類(lèi)支持這個(gè)協(xié)議罪裹。
- 我們的數(shù)據(jù)源中的每個(gè)數(shù)據(jù)對(duì)象(model)必須具備唯一辨認(rèn)標(biāo)識(shí)饱普。
- 使用這個(gè)協(xié)議的方法去保證相同的數(shù)據(jù)對(duì)象,(而不僅僅是相同的行的索引)被滾動(dòng)到視圖并且被選中状共。舉個(gè)場(chǎng)景的例子:TableView的數(shù)據(jù)源對(duì)象在上次保存時(shí)套耕,所保存的行的索引,可能會(huì)因?yàn)樵诋?dāng)前運(yùn)行周期內(nèi)數(shù)據(jù)源中數(shù)據(jù)的變動(dòng)發(fā)生變化峡继。從而導(dǎo)致當(dāng)前選中的行所對(duì)應(yīng)的數(shù)據(jù)并非上次保存時(shí)的數(shù)據(jù)冯袍。
- 若需要使用
UIDataSourceModelAssociation
,則:實(shí)現(xiàn)了UITableView 和 UICollectionView數(shù)據(jù)源協(xié)議的對(duì)象,負(fù)責(zé)實(shí)現(xiàn)這個(gè)協(xié)議的方法,否則不會(huì)生效康愤。實(shí)際操作發(fā)現(xiàn)確實(shí)如此儡循。
除了官網(wǎng)解釋,在實(shí)際操作中發(fā)現(xiàn)還需要設(shè)置UITableView 或UICollectionView的restorationIdentifier征冷,否則UIDataSourceModelAssociation協(xié)議方法不會(huì)被調(diào)用
择膝。關(guān)于UITableView的restorationIdentifier查閱官方文檔如下:
To save and restore the table’s data, assign a nonempty value to the table view’s restorationIdentifier property. When its parent view controller is saved, the table view automatically saves the index paths for the currently selected and visible rows. If the table’s data source object adopts the UIDataSourceModelAssociation protocol, the table stores the unique IDs that you provide for those items instead of their index paths.
UITableView設(shè)置了restorationIdentifier
,進(jìn)行UI的保存時(shí)检激,tableView會(huì)自動(dòng)存儲(chǔ)當(dāng)前選中和可見(jiàn)行的索引肴捉。補(bǔ)充:還會(huì)存儲(chǔ)滾動(dòng)偏移,并可以恢復(fù)叔收。
UIDataSourceModelAssociation使用
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
InfoModel *model = [self.dataSource objectAtIndex:indexPath.row];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass(UITableViewCell.class) forIndexPath:indexPath];
cell.textLabel.text = model.title;
cell.restorationIdentifier = model.identifier;
return cell;
}
- (NSString *)modelIdentifierForElementAtIndexPath:(NSIndexPath *)idx inView:(UIView *)view {
//根據(jù)index 返回identifier
NSString *identifier = nil;
InfoModel *model = [self.dataSource objectAtIndex:idx.row];
/*
注釋①
if (idx && view) {
identifier = model.identifier;
}
*/
if (idx.row == _currentPath.row && view) {
identifier = model.identifier;
}
//若是不定義_currentPath追蹤當(dāng)前選中的cell.會(huì)多保存一個(gè)cell齿穗,目前尚未有答案。
return identifier;
}
//此方法 恢復(fù)時(shí)調(diào)用
- (NSIndexPath *)indexPathForElementWithModelIdentifier:(NSString *)identifier inView:(UIView *)view {
//根據(jù)identifier 返回index;
NSIndexPath *indexPath = nil;
if (identifier && view) {
__block NSInteger row = 0;
[self.dataSource enumerateObjectsUsingBlock:^(InfoModel *obj, NSUInteger idx, BOOL * _Nonnull stop) {
if ([obj.identifier isEqualToString:identifier]) {
row = idx;
*stop = YES;
}
}];
indexPath = [NSIndexPath indexPathForRow:row inSection:0];
_currentPath = indexPath;
NSLog(@"當(dāng)前選中的數(shù)據(jù)源對(duì)象標(biāo)識(shí)是:%@,對(duì)象抬頭是:%@",[self.dataSource[indexPath.row] identifier],[self.dataSource[indexPath.row] title]);
}
return indexPath;
}
上述代碼方法-(NSString *)modelIdentifierForElementAtIndexPath:(NSIndexPath *)idx inView:(UIView *)view
中注釋①描述:此方法會(huì)在保存時(shí)調(diào)用兩次今穿,idx所返回的數(shù)據(jù)除了我們選中的行缤灵,還會(huì)返回一個(gè)其他行。 若是采用這種方式映射唯一標(biāo)識(shí)蓝晒,會(huì)出現(xiàn)保存了我們不需要的行的標(biāo)識(shí)腮出,導(dǎo)致恢復(fù)滑動(dòng)位置失效,針對(duì)此問(wèn)題目前筆者尚未有答案芝薇,查閱資料發(fā)現(xiàn)這個(gè)問(wèn)題曾經(jīng)是蘋(píng)果的一個(gè)BUG胚嘲,若是大家知道具體原因,歡迎評(píng)論和補(bǔ)充洛二。目前在此基礎(chǔ)上筆者自己想的解決辦法:定義_currentPath追蹤當(dāng)前選中的cell馋劈,保存時(shí)根據(jù)_currentPath保存我們需要的標(biāo)識(shí),測(cè)試中發(fā)現(xiàn)可以解決問(wèn)題晾嘶。
QIRestorationDemo地址