美文网首页移动开发精华
iOS - Controller 瘦身简析

iOS - Controller 瘦身简析

作者: 豆志昂扬 | 来源:发表于2017-08-12 20:57 被阅读56次

《iOS 常见架构一览》中提到, 由于iOS 开发模式中没有在设计上规范的子组件所在位置,若使用不当,会导致UIViewController过于臃肿。 下面聊聊几种给Controller瘦身的办法。

KeyBoard Manager

很多页面都需要在键盘状态改变后更新视图的状态,这是一个经典的非核心代码堆砌在Controller的样例。我们可以尝试定义一个简单的键盘事件管理类实现和Controller的分离。

@implementation EDKeyboardManager : NSObject

- (instancetype)initWithTableView:(UITableView *)tableView {
    self = [super init];
    if (!self) return nil;
    _tableView = tableView;
    return self;
}

- (void)addObservingKeyboard {
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDidHide:) name:UIKeyboardDidHideNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
}

- (void)removeObservingKeyboard {
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardDidHideNotification object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
}

- (void)keyboardWillShow:(NSNotification *)note {
    CGRect keyboardRect = [[note.userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];

    UIEdgeInsets contentInsets = UIEdgeInsetsMake(self.tableView.contentInset.top, 0.0f, CGRectGetHeight(keyboardRect), 0.0f);
    self.tableView.contentInset = contentInsets;
    self.tableView.scrollIndicatorInsets = contentInsets;
}

- (void)keyboardDidHide:(NSNotification *)note {
    UIEdgeInsets contentInset = UIEdgeInsetsMake(self.tableView.contentInset.top, 0.0f, self.oldBottomContentInset, 0.0f);
    self.tableView.contentInset = contentInset;
    self.tableView.scrollIndicatorInsets = contentInset;
}

@end

这里也可以使用一些成熟的类库,如iOS开发第三方库一 IQKeyboardManager

View Controller 组合

如果一个View Controller由一些逻辑独立,可复用的视图组成,可以尝试通过分割成多个子View Controller, 而多个子View Controller通过iOS
暴露的原生API组装成目标视图。

//在ViewController 中添加其他UIViewController,currentVC是一个UIViewController变量,存储当前显示的viewcontroller
 FirstVC * first = [[FirstVC alloc] init];
[self addChildViewController:first];
//addChildViewController 会调用 [child willMoveToParentViewController:self] 方法,但是不会调用 didMoveToParentViewController:方法,官方建议显示调用
[first didMoveToParentViewController:self];
[first.view setFrame:CGRectMake(0, CGRectGetMaxY(myScrollView.frame), width, height-CGRectGetHeight(myScrollView.frame))];
[self.view addSubview:currentVC.view];

SecondVC * second = [[SecondVC alloc] init];
[self addChildViewController:second];
[self didMoveToParentViewController:second];
[second.view setFrame:CGRectMake(0,CGRectGetMaxY(myScrollView.frame), width, height-CGRectGetHeight(myScrollView.frame))];

MVP 模式

在MVP模式中, Presenter 通过桥接 Model和 View, 使View Controller不再直接操作Model, 从而组件之间耦合度更高,代码后续维护更容易。

class UserPresenter {
  //获取User数据
    private let userService:UserService
    weak private var userView : UserView?
     
    init(view:UserView){
        userView = view
    }
     
    func getUsers(){
        self.userView?.startLoading()
        userService.getUsers{ [weak self] users in
            self?.userView?.finishLoading()
            if(users.count == 0){
                self?.userView?.setEmptyUsers()
            }else{
                let mappedUsers = users.map{
                    return UserViewData(name: "\($0.firstName) \($0.lastName)", age: "\($0.age) years")
                }
                self?.userView?.setUsers(mappedUsers)
            }
             
        }
    }
}

MVVM模式

其实这里着重介绍的是数据绑定,而MVVM是通过数据绑定View和Model, 实现两者之间状态自动同步的典型。实现数据绑定的方式很多,KVO, KVC, ReactiveCocoa 等, 有兴趣的可以阅读文章MVVM和数据绑定

class ViewController: UIViewController {
    @IBOutlet private weak var userLabel: UILabel!
    private let viewModel: ViewModel
    private let disposeBag: DisposeBag

    private func bindToViewModel() {
        viewModel.myProperty
            .drive(userLabel.rx.text)
            .disposed(by: disposeBag)
    }
}

数据源

iOS开发者对于UITableView 和 UITableViewDataSource 应该不会陌生,对于一个相对复杂的UITableView,很有必要把UITableViewDataSource的实现和View Controller分离开来,同时也让复用有了可能。

class IceCreamListDataSource: NSObject, UITableViewDataSource
{
  let dataStore = IceCreamStore()
 
  // MARK: - Table view data source
 
  func numberOfSectionsInTableView(tableView: UITableView) -> Int
  {
    return 1
  }
 
  func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
  {
    return dataStore.allFlavors().count
  }
 
  func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
  {
    let flavor = dataStore.allFlavors()[indexPath.row]
    let cell = tableView.dequeueReusableCellWithIdentifier("IceCreamListCell", forIndexPath: indexPath)
    cell.textLabel?.text = flavor
    return cell
  }
}
 

最后

以上的种种策略,只是在开发者认为遇到上述问题时的解决策略,在决定是否重构代码之前,结合其可复用性,易用性和可维护性权衡利弊,防止走向"过分设计的"极端。

更多

获取更多内容请关注微信公众号豆志昂扬:

  • 直接添加公众号豆志昂扬
  • 微信扫描下图二维码;

相关文章

  • iOS - Controller 瘦身简析

    在《iOS 常见架构一览》中提到, 由于iOS 开发模式中没有在设计上规范的子组件所在位置,若使用不当,会导致UI...

  • Controller瘦身

    在我们使用传统的MVC设计模式的时候,通常写着写着C中的代码就会变得又多又乱,今天通过一个简单的实例来了解MVP设...

  • iOS 开发中为controller 瘦身

    在以往的开发中,使用MVC架构模式的时候还是居多的,MVC方便的是职责比较明确,View负责界面的展示,Model...

  • iOS代码瘦身实践

    iOS代码瘦身实践 iOS代码瘦身实践

  • Controller 的瘦身

    讨论下Controller 瘦身.在此之前一直想把tableview的代理在controller中干掉 ,可是一直...

  • react native资料

    React Native 学习资源精选仓库(汇聚知识,分享精华) React Native IOS集成与原理简析 ...

  • 2020-04-08

    iOS Appium自动化测试框架原理简析 原文链接:https://easeapi.com/blog/blog/...

  • iOS WebViewJavascriptBridge源码解析

    iOS WebViewJavascriptBridge源码解析一 用法简析 这个库的还是比较精简单的,当前webV...

  • iOS 枚举简析

    前言 在之前的一篇文章中简单的提到了这个问题, 但是自己写的不详细, 并且自己深入了解的也不是特别多, 在开发中也...

  • iOS Runloop简析

    1. 什么是Runloop? Runloop是通过内部维持事件循环来对事件/消息进行管理的一个对象; 2.runl...

网友评论

    本文标题:iOS - Controller 瘦身简析

    本文链接:https://www.haomeiwen.com/subject/zexlrxtx.html