玩轉iOS開發:iOS 11 新特性《UIKit新特性的基本認識》

CainLuo發表於2017-11-07

文章分享至我的個人技術部落格: https://cainluo.github.io/15099354591154.html


前兩篇, 我們講了Xcode 9的一些新特性, 可以更加方便我們去寫"bug".

如果沒有看的朋友可以去看看:

那麼這一次呢, 我們來簡單的瞭解一下, 在iOS 11裡, UIKit更新了一些什麼東西, 可以讓我們更加快捷的開發.

轉載宣告:如需要轉載該文章, 請聯絡作者, 並且註明出處, 以及不能擅自修改本文.


Paste configuration

我們都知道, 在iOS有一個東西叫做UIMenuController, 它是一個單例, 可以方便我們簡單的去做一些複製, 貼上等等的操作, 但在iOS 11這個貼上功能進化了, 讓我們一起來看看吧, 首先我們要有一個工程專案:

1

簡單顯示的東西就好.

然後呢, 我們需要有一個用手勢操作UIMenuController的管理者MenuManager, 詳細的程式碼都在工程裡, 大家可以去看看.

iOS 11的時候, 蘋果推出了一個東西叫做UIPasteConfiguration, 它是直接繼承NSObject官方解釋:

The interface that an object implements to declare its ability to accept specific data types for pasting and for drag and drop activities.

這個東西能用來幹嘛呢?


配置貼上功能

在專案裡, 我們預設給TableView加了長按手勢和點選手勢, 便於用來控制UIMenuController.

為了方便演示這個配置貼上的功能, 另外多開了一個控制器, 這裡我們需要宣告一個全域性字串, 為的就是用來給這個貼上功能加個唯一的標識:

@property (nonatomic, copy) NSArray<NSString *> *acceptableTypeIdentifiers;
複製程式碼

新增了這個唯一標識, 我們就需要在Cell裡給copy:的方法加點修改:

- (BOOL)canBecomeFirstResponder {
    
    return YES;
}

- (BOOL)canPerformAction:(SEL)action
              withSender:(id)sender {
    
    return action == @selector(copy:);
}

- (void)copy:(id)sender {
    
    if (self.model == nil) {
        return;
    }
    
    NSData *data = [NSKeyedArchiver archivedDataWithRootObject:self.model];
    
    [[UIPasteboard generalPasteboard] setData:data
                            forPasteboardType:CELL_TYPE];
}
複製程式碼

這裡面的CELL_TYPE就是全域性的唯一標識.

還有就是要在需要貼上功能的控制器PasteViewController裡也配置一下:

    UIPasteConfiguration *pasteConfiguration = [[UIPasteConfiguration alloc] initWithAcceptableTypeIdentifiers:@[CELL_TYPE]];
        
    self.pasteConfiguration = pasteConfiguration;
複製程式碼

並且這裡我們還要重寫一個方法:

- (void)pasteItemProviders:(NSArray<NSItemProvider *> *)itemProviders {
    
    for (NSItemProvider *item in itemProviders) {
        
        [item loadObjectOfClass:TableViewCellModel.class
              completionHandler:^(id<NSItemProviderReading>  _Nullable object, NSError * _Nullable error) {
                 
                 if (object) {
                     
                     TableViewCellModel *model = (TableViewCellModel *)object;
                     
                     dispatch_async(dispatch_get_main_queue(), ^{
                         
                         self.targetTextField.text = [NSString stringWithFormat:@"複製的內容: %@", model.titleString];
                     });
                 }
             }];
    }
}
複製程式碼

用來處理貼上後的資料.

PS: 這裡的TableViewCellModel是需要遵守一個NSItemProviderReading協議, 並且在內部實現它的協議方法, 詳情可以去程式碼裡看看.


拖放的基本認識

iOS 11, 蘋果爸爸終於把拖放的功能新增進來了, 但這個功能真正受益的是iPad, 它可以在分屏App裡實現資料複製和移動, 甚至還可以共享.

我們在拖動的過程中, 資料會被序列化, 然後呈現在使用者拖動的系統控制預覽中, 在拖動完成後, 序列化的資料會被複制到目的地, 然後反序列化, 最終將這些資訊呈獻給使用者.

而最先獲得支援的兩個控制元件就是UITableViewUICollectionView. 現在讓我們來新建個工程看看是如何操作.

這裡我們還是一個簡單的TableView:

2

這裡我們要介紹兩個新的代理UITableViewDragDelegate, 和UITableViewDropDelegate, 一個分別拖動, 一個分別是放下的代理.

這裡我們要宣告一個遵守了NSItemProviderReading, NSItemProviderWritingModel, 內部實現在工程裡檢視:

最終我們去實現拖放功能的就是要去實現那兩個代理的方法:

#pragma mark - Table View Drag Delegate
- (NSArray<UIDragItem *> *)tableView:(UITableView *)tableView
        itemsForBeginningDragSession:(id<UIDragSession>)session
                         atIndexPath:(NSIndexPath *)indexPath {
    
    ListModel *model = self.dataSource[indexPath.row];
    
    NSItemProvider *itemProvider = [[NSItemProvider alloc] initWithObject:model];
    
    UIDragItem *dragItem = [[UIDragItem alloc] initWithItemProvider:itemProvider];
    
    return @[dragItem];
}

#pragma mark - Table View Drop Delegate
- (void)tableView:(UITableView *)tableView
performDropWithCoordinator:(id<UITableViewDropCoordinator>)coordinator {
    
    if (!coordinator) {
        return;
    }
    
    NSIndexPath *destinationIndexPath = coordinator.destinationIndexPath;
    
    dispatch_async(dispatch_get_main_queue(), ^{
        
        [tableView performBatchUpdates:^{
            
            [coordinator.items enumerateObjectsUsingBlock:^(id<UITableViewDropItem>  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
                
                if (!obj) {
                    return;
                }
                
                NSIndexPath *indexPath = obj.sourceIndexPath;

                ListModel *model = self.dataSource[indexPath.row];

                [self.dataSource removeObject:model];
                [self.dataSource insertObject:model
                                      atIndex:destinationIndexPath.row];

                [tableView moveRowAtIndexPath:indexPath
                                  toIndexPath:destinationIndexPath];
            }];
            
        } completion:nil];
    });
}

- (BOOL)tableView:(UITableView *)tableView
canHandleDropSession:(id<UIDropSession>)session {
    return [session canLoadObjectsOfClass:ListModel.class];
}

- (UITableViewDropProposal *)tableView:(UITableView *)tableView
                  dropSessionDidUpdate:(id<UIDropSession>)session
              withDestinationIndexPath:(nullable NSIndexPath *)destinationIndexPath {
    
    return [[UITableViewDropProposal alloc] initWithDropOperation:UIDropOperationMove
                                                           intent:UITableViewDropIntentInsertAtDestinationIndexPath];
}
複製程式碼

程式碼寫完了之後, 別忘了把TableView的拖放功能給開啟:

        _tableView.dragInteractionEnabled = YES;
複製程式碼

效果圖這裡就不放了, 自己去跑跑Demo就好了, 個人認為自己寫的程式碼還是挺規整的~~哈哈

更詳細的內容會在後續的文章慢慢講解.


TableView側邊欄的改進

iOS 8的時候, 蘋果爸爸就為TableView引進了側邊欄的功能, 叫做UITableViewRowAction, 不過在iOS 11的時候, 蘋果爸爸又增加了一個更加靈活的東西, 叫做UISwipeActionsConfiguration:

我們另外建一個工程來看看, 然後實現我們的配置:

- (UISwipeActionsConfiguration *)tableView:(UITableView *)tableView trailingSwipeActionsConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath {
    
    UIContextualAction *contextualAction = [UIContextualAction contextualActionWithStyle:UIContextualActionStyleNormal
                                                                                   title:@"Add"
                                                                                 handler:^(UIContextualAction * _Nonnull action, __kindof UIView * _Nonnull sourceView, void (^ _Nonnull completionHandler)(BOOL)) {
                                                                                     
                                                                                     NSLog(@"Add");
                                                                                 }];
    
    contextualAction.backgroundColor = [UIColor brownColor];
    
    UISwipeActionsConfiguration *swipeActionsCOnfiguration = [UISwipeActionsConfiguration configurationWithActions:@[contextualAction]];
    
    return swipeActionsCOnfiguration;
}

- (UISwipeActionsConfiguration *)tableView:(UITableView *)tableView
leadingSwipeActionsConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath {
    
    UIContextualAction *contextualAction = [UIContextualAction contextualActionWithStyle:UIContextualActionStyleNormal
                                                                                   title:@"Copy"
                                                                                 handler:^(UIContextualAction * _Nonnull action, __kindof UIView * _Nonnull sourceView, void (^ _Nonnull completionHandler)(BOOL)) {
                                                                                     
                                                                                     NSLog(@"Copy");
                                                                                 }];
    
    contextualAction.backgroundColor = [UIColor blackColor];
    
    UISwipeActionsConfiguration *swipeActionsCOnfiguration = [UISwipeActionsConfiguration configurationWithActions:@[contextualAction]];
    
    return swipeActionsCOnfiguration;
}
複製程式碼

3
4

關於TableView重新整理的時候, 我們還有一個專門的API:

- (void)performBatchUpdates:(void (NS_NOESCAPE ^ _Nullable)(void))updates completion:(void (^ _Nullable)(BOOL finished))completion API_AVAILABLE(ios(11.0), tvos(11.0));
複製程式碼

剛剛的拖放功能也是在這裡面完成重新整理資料來源的, 這麼做的話, 可以讓我們的邏輯結構更加的清晰, 這樣子我們也需要在使用dispatch_async(dispatch_get_main_queue(), ^{});去更新了, 爽爽滴~~


Asset UIColor的整合

Xcode 9裡, Asset裡可以整合UIColor的目錄, 這樣子我們就可以省略宣告一大堆的顏色, 詳細怎麼做呢? 我們一起來看看, 這裡隨便弄一個專案就好了.

然後我們在Assets.xcassets裡新增Color Set:

5

然後新增自己喜歡的顏色值:

6

7

這裡我們要看看兩個API, 都是在iOS 11之後才出來的

+ (nullable UIColor *)colorNamed:(NSString *)name

+ (nullable UIColor *)colorNamed:(NSString *)name inBundle:(nullable NSBundle *)bundle compatibleWithTraitCollection:(nullable UITraitCollection *)traitCollection
複製程式碼

最終效果:

8


新新增的輔助功能

在講這個得時候, 這裡是需要裝有iOS 11的真機裝置.

但由於我現在手上沒有iOS 11的裝置, 所以這裡暫時不說, 有興趣的話, 可以到百度去搜搜, 或者等我iOS 11裝置的時候再更新吧.


總結

iOS 11更多的東西都是在優化和改進開發的流程, 這篇張文章只是簡單的介紹一下而已, 還有更多更深層次的可以自行去查閱蘋果的官方文件, 或者是去看看WWDC的視訊演示:


工程地址

專案地址: https://github.com/CainRun/iOS-11-Characteristic/tree/master/2.UIKit


最後

碼字很費腦, 看官賞點飯錢可好

微信

支付寶

相關文章