Lighter View Controllers For LEAFPhoto

[LEAF Photo] 整个 app 的大部分功能都是在 LPCollageViewController 中实现的,随着时间的推移,这个 Controller 开始变得臃肿代码很长,记录下瘦身的各种策略。

ViewModel 及组合模式的使用

[LEAF Photo] 为用户提供了拼接图片的不同模板,模板中包含了选择照片的视图、文字视图以及线条等,模板以 json 文件方式保存,将这些元素显示到视图上简单的逻辑就是判断 json 中是否存在节点,如果存在则创建对应的元素,这样创建模板的过程就会变的臃肿。

借鉴下 MVVM 的方式,将 MVC 中的 C 拆分成 VM,使用 ViewModel 的方式处理展示,在利用组合的方式将每种元素的展示方法拆分到各自类中实现,这样创建元素的这个冗长的过程就可以被分散到每个元素的 ViewModel 类中了。

@interface CollageComponentViewModel : NSObject
- (void)showToView:(CollageContainerView *)view target:(id)target;
@end
@interface CollageViewModel : CollageComponentViewModel
@end
@implementation CollageViewModel
- (instancetype)initWithCollageModel:(CollageModel *)collageModel {
    self = [super init];
    if (self) {
        _collageModel = collageModel;
    }
    return self;
}
- (void)showToView:(CollageContainerView *)view target:(id)target {
    NSArray *textDecArray = self.collageModel.textDec;
    if (textDecArray) {
        [self showToView:view target:target array:textDecArray];
    }
    // ...
}
@end
@interface CollageTextDecViewModel : CollageComponentViewModel
@end
- (void)showToView:(CollageContainerView *)view target:(id)target {
    CollageLabel *textLabel = [[CollageLabel alloc] initWithFrame:frame];
    textLabel.text = self.collageTextDecModel.text;
    textLabel.font = [UIFont fontWithName:self.collageTextDecModel.fontName size:fontSize];
    // ...
    [view addSubview:textLabel];
}

原本在 Controller 和 View 中的很多代码,被分散到 CollageTextDecViewModel 等 ViewModel 中去。

除了此例外,通常的应用中一些网络请求的处理和页面设置也可以使用 ViewModel 的方式。

将子视图独立成单独的类

子视图独立成一个类,并使用懒加载的方式创建。

@interface CollageViewController () 
@property (strong, nonatomic) CollageTextToolbar *textToolbar;
@end
@implementation CollageViewController
- (CollageTextToolbar *)textToolbar {
    if (_textToolbar) {
        return _textToolbar;
    }
    _textToolbar = [[CollageTextToolbar alloc] init];
    [self.view addSubview:_textToolbar];
}
// somewhere call self.textToolbar to set its frame
@end
@implementation CollageTextToolbar
- (UIView *)editOuterView {
    if (_editOuterView) {
        return _editOuterView;
    }
    _editOuterView = [[UIView alloc] init];
    [_editOuterView setBackgroundColor:[UIColor whiteColor]];
    [self addSubview:_editOuterView];
    return _editOuterView;
}
- (UITextView *)editingTextView {
    if (_editingTextView) {
        return _editingTextView;
    }
    _editingTextView = [[UITextView alloc] init];
    _editingTextView.delegate = self;
    [self.editOuterView addSubview:_editingTextView];
    return _editingTextView;
}
@end

将视图的处理抽离出来,Controller 只负责初始化设置属性,这样 CollageTextToolbar 中大量的视图设置代码可以从 Controller 中移除。

CollageTextToolbar 中存在的按钮,如果只需要和本视图中的控件打交道最好,如果还需要 Controller 中的视图进行交互,使用协议即可。

Keyboard Manager

除了视图,一些功能也可以抽离成一个单独的处理类。

@implementation LPCollageKeyboardManager : NSObject
- (instancetype)initWithTextToolBar:(CollageTextToolbar *)textToolBar {
    self = [super init];
    if (self) {
        _textToolBar = textToolBar;
    }
    return self;
}
- (void)addKeyboardObserver {
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector( keyboardWillHide:)  
                                                 name:UIKeyboardWillHideNotification 
                                               object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self 
                                             selector:@selector(keyboardWillShow:) 
                                                 name:UIKeyboardWillShowNotification 
                                               object:nil];
}
- (void)removeKeyboardObserver {
    [[NSNotificationCenter defaultCenter] removeObserver:self 
                                                    name:UIKeyboardWillHideNotification 
                                                    object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self
                                                    name:UIKeyboardWillShowNotification 
                                                    object:nil];
}
- (void)keyboardWillShow:(NSNotification *)notification {
    // ... 设置 self.textToolBar
}
- (void)keyboardWillShow:(NSNotification *)notification {
    // ... 设置 self.textToolBar
}
@end

另外在页面中很多按钮触发跳转逻辑,可以将 NavigationController 的跳转逻辑独立成一个类

@interface LPCollageNavigator
- (void)navigateToDoneController:(UIImage *)savedImage;
@end
@implementation LPCollageNavigator : NSObject
- (instancetype)initWithNavigationController:(UINavigationController *)navigationController {
    self = [super init];
    if (self) {
       _navigationController = navigationController; 
    }
    return self;
}
- (void)navigateToDoneController:(UIImage *)savedImage {
    LPCollageDoneController *doneController = [[LPCollageDoneController alloc] initWithSavedImage:savedImage];
    [self.navigationController pushViewController:doneController  animated:YES];
}
@end

将 DataSource 和其他 Protocols 分离

另外可以参见 objc.ic 的 Controller 瘦身 文章为其瘦身

@implementation ArrayDataSource
- (id)itemAtIndexPath:(NSIndexPath*)indexPath {
    return items[(NSUInteger)indexPath.row];
}
- (NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section {
    return items.count;
}
- (UITableViewCell*)tableView:(UITableView*)tableView 
    cellForRowAtIndexPath:(NSIndexPath*)indexPath {
    id cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier
                                          forIndexPath:indexPath];
    id item = [self itemAtIndexPath:indexPath];
    configureCellBlock(cell,item);
    return cell;
}
@end
@implementation CustomTableViewController
void (^configureCell)(PhotoCell*, Photo*) = ^(PhotoCell* cell, Photo* photo) {
    cell.label.text = photo.name;
};
photosArrayDataSource = [[ArrayDataSource alloc] initWithItems:photos
                                            cellIdentifier:PhotoCellIdentifier
                                        configureCellBlock:configureCell];
self.tableView.dataSource = photosArrayDataSource;
@end
请我喝汽水儿