彩票走势图

8个模式帮你消除iOS代码中的巨大View Controller

转帖|行业资讯|编辑:龚雪|2016-07-25 10:15:04.000|阅读 356 次

概述:随着功能的累计,View Controller的体量会变得巨大。键盘管理、用户输入、数据变形、视图分配——这些东西当中哪个才是真正的View Controller范围?哪些东西应该指派给其他对象?在这篇文章中,我们将会探索将这些职责隔离进其各自对象的方式。这样做能帮助我们简化代码,让代码获得更高的可读性。

# 慧都年终大促·界面/图表报表/文档/IDE等千款热门软控件火热促销中 >>

在一个ViewController中,这些职责可以被统一放在#pragma区域中。但是,我们其实应该考虑将它拆分,并且放在更小的原件中。

数据源

数据源模式(Data Source Pattern)是一种用来隔离哪个对象对应哪个引导路径的逻辑的方式。尤其是在复杂的图标视图中,这个模式非常实用,可以用来移除View Controller里所有“哪些cell在特定条件下可见”的逻辑。如果你曾经写过这样的图标,经常需要对row和section的整数进行对比,那么数据源模式非常适合你。

数据源模式可以和UITableViewDataSource共存,但是我发现用这些对象对cell进行配置,其发挥的作用于管理引导路径时不太一样,因此我比较喜欢将两者分开。

这个简单的数据源模式使用实例,可以帮你处理分段逻辑:

@implementation SKSectionedDataSource : NSObject

 

- (instancetype)initWithObjects:(NSArray*)objects sectioningKey:(NSString *)sectioningKey {

    self = [super init];

    if (!self) return nil;

 

    [self sectionObjects:objectswithKey:sectioningKey];

 

    return self;

}

 

-(void)sectionObjects:(NSArray *)objects withKey:(NSString *)sectioningKey {

    self.sectionedObjects = //section theobjects array

}

 

-(NSUInteger)numberOfSections {

    return self.sectionedObjects.count;

}

 

-(NSUInteger)numberOfObjectsInSection:(NSUInteger)section {

    return [self.sectionedObjects[section]count];

}

 

-(id)objectAtIndexPath:(NSIndexPath *)indexPath {

    returnself.sectionedObjects[indexPath.section][indexPath.row];

}

 

@end

标准合成(Standard Composition)

苹果在发布iOS5的时候,一同推出了View Controller Containment API。你可以使用这个API对View Controller进行合成。如果你的ViewController由多个逻辑单元所构成,你可以考虑将其拆分。

在一个拥有header和grid视图的屏幕上,我们可以加载两个View Controller,然后将他们放在正确的位置上。

-(SKHeaderViewController *)headerViewController {

    if (!_headerViewController) {

        SKHeaderViewController*headerViewController = [[SKHeaderViewController alloc] init];

 

        [selfaddChildViewController:headerViewController];

        [headerViewControllerdidMoveToParentViewController:self];

 

        [self.viewaddSubview:headerViewController.view];

 

        self.headerViewController =headerViewController;

    }

    return _headerViewController;

}

 

-(SKGridViewController *)gridViewController {

    if (!_gridViewController) {

        SKGridViewController*gridViewController = [[SKGridViewController alloc] init];

 

        [selfaddChildViewController:gridViewController];

        [gridViewControllerdidMoveToParentViewController:self];

 

        [self.viewaddSubview:gridViewController.view];

 

        self.gridViewController =gridViewController;

    }

    return _gridViewController;

}

 

-(void)viewDidLayoutSubviews {

    [super viewDidLayoutSubviews];

 

    CGRect workingRect = self.view.bounds;

 

 CGRect headerRect = CGRectZero, gridRect =CGRectZero;

    CGRectDivide(workingRect, &headerRect,&gridRect, 44, CGRectMinYEdge);

 

   self.headerViewController.view.frame = tagHeaderRect;

    self.gridViewController.view.frame =hotSongsGridRect;

}

Smarter Views

如果你是在ViewController的类中对所有子视图进行分配,你可以考虑使用Smarter View。UIViewController默认情况下会使用UIView来浏览属性,但是你也可以用自己的视图去取代它。你可以使用-loadView作为接入点,前提是你要在那个方法中设定了self.view。

@implementationSKProfileViewController

 

- (void)loadView {

    self.view = [SKProfileView new];

}

 

//...

 

@end

 

@implementationSKProfileView : NSObject

 

- (UILabel *)nameLabel {

    if (!_nameLabel) {

        UILabel *nameLabel = [UILabel new];

        //configure font, color, etc

        [self addSubview:nameLabel];

        self.nameLabel = nameLabel;

    }

    return _nameLabel;

}

 

- (UIImageView*)avatarImageView {

    if (!_avatarImageView) {

        UIImageView * avatarImageView =[UIImageView new];

        [self addSubview:avatarImageView];

        self.avatarImageView = avatarImageView;

    }

    return _avatarImageView

}

 

-(void)layoutSubviews {

    //perform layout

}

 

@end

你也可以重新定义@property(nonatomic) SKProfileView *view,因为它是一个比UIView更具体的类别,分析器会将self.view视为 SKProfileView,从而完成正确的处理。

Presenter模式

Presenter模式可以包裹模型对象,改变它的显示属性,并且公开那些已被改变的属性的消息。在其他一些情境中,它也被称为Presentation Model、Exhibit模式和ViewModel等。

@implementation SKUserPresenter : NSObject

 

-(instancetype)initWithUser:(SKUser *)user {

    self = [super init];

    if (!self) return nil;

    _user = user;

    return self;

}

 

- (NSString *)name{

    return self.user.name;

}

 

- (NSString *)followerCountString{

    if (self.user.followerCount == 0) {

        return @"";

    }

    return [NSString stringWithFormat:@"%@followers", [NSNumberFormatterlocalizedStringFromNumber:@(_user.followerCount)numberStyle:NSNumberFormatterDecimalStyle]];

}

 

- (NSString*)followersString {

    NSMutableString *followersString =[@"Followed by " mutableCopy];

    [followersStringappendString:[self.class.arrayFormatter stringFromArray:[self.user.topFollowersvalueForKey:@"name"]];

    return followersString;

}

 

+(TTTArrayFormatter*) arrayFormatter {

    static TTTArrayFormatter *_arrayFormatter;

    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{

        _arrayFormatter = [[TTTArrayFormatteralloc] init];

       _arrayFormatter.usesAbbreviatedConjunction = YES;

    });

    return _arrayFormatter;

}

 

@end

最重要的是,模型对象本身不会被暴露。Presenter扮演了模型看门人的角色。这保证了View Controller无法绕开Presenter而直接访问模型。

Binding模式

Binding模式在变化的过程中会使用模型数据对视图进行更新。Cocoa非常适合使用这个模式,因为KVO能够观察模型,并且从模型中进行读取,在视图中完成写入。Cocoa Binding是这个模式的AppKit版本。Reactive Cocoa等第三方库也非常适合这个模式。

@implementationSKProfileBinding : NSObject

 

-(instancetype)initWithView:(SKProfileView *)view presenter:(SKUserPresenter*)presenter {

    self = [super init];

    if (!self) return nil;

    _view = view;

    _presenter = presenter;

    return self;

}

 

- (NSDictionary*)bindings {

    return @{

              @"name":@"nameLabel.text",

              @"followerCountString":@"followerCountLabel.text",

            };

}

 

- (void)updateView{

    [self.bindingsenumerateKeysAndObjectsUsingBlock:^(id presenterKeyPath, id viewKeyPath, BOOL*stop) {

        id newValue = [self.presentervalueForKeyPath:presenterKeyPath];

        [self.view setObject:newvalueforKeyPath:viewKeyPath];

    }];

}

 

@end

Interaction模式

View Controller变得体量过大的重要原因之一,就是actionSheet.delegate= self的滥用。在Smaitalk中,Controller对象的整个角色,就是接受用户输入,并且更新试图和模型。如今我们所使用的交互相对复杂,这些交互会要求我们在View Controller中写下大量的代码。

交互的过程通常开始与用户的最初输入(例如点击按钮)、可选的用户再次输入(例如“你确定要继续吗?”),之后程序或产生活动,例如网路请求和状态改变。这个操作其实可以完全包裹在Interaction Object之中。

 @implementationSKProfileViewController

 

- (void)followButtonTapped:(id)sender{

    self.followUserInteraction =[[SKFollowUserInteraction alloc] initWithUserToFollow:self.user delegate:self];

    [self.followUserInteraction follow];

}

 

-(void)interactionCompleted:(SKFollowUserInteraction *)interaction {

    [self.binding updateView];

}

 

//...

 

@end
@implementationSKFollowUserInteraction : NSObject 

 

-(instancetype)initWithUserToFollow:userdelegate:(id)delegate {

    self = [super init];

    if !(self) return nil;

    _user = user;

    _delegate = delegate;

    return self;

}

 

- (void)follow {

    [[[UIAlertView alloc] initWithTitle:nil

                               message:@"Are you sure you want to follow this user?"

                               delegate:self

                     cancelButtonTitle:@"Cancel"

                     otherButtonTitles:@"Follow", nil] show];

}

 

-(void)alertView:(UIAlertView *)alertViewclickedButtonAtIndex:(NSInteger)buttonIndex {

    if ([alertView buttonTitleAtIndex:buttonIndex]isEqual:@"Follow"]) {

        [self.user.APIGatewayfollowWithCompletionBlock:^{

            [self.delegateinteractionCompleted:self];

        }];

    }

}

 

@end

Keyboard Manager

当键盘状态出现改变,视图的更新也会在View Controller中出现卡顿,但是使用KeyboardManager模式可以很好的解决这个问题。

@implementationSKNewPostKeyboardManager : NSObject

 

-(instancetype)initWithTableView:(UITableView *)tableView {

    self = [super init];

    if (!self) return nil;

    _tableView = tableView;

    return self;

}

 

- (void)beginObservingKeyboard{

    [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(keyboardDidHide:)name:UIKeyboardDidHideNotification object:nil];

    [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(keyboardWillShow:)name:UIKeyboardWillShowNotification object:nil];

}

 

-(void)endObservingKeyboard {

    [[NSNotificationCenter defaultCenter]removeObserver:self name:UIKeyboardDidHideNotification object:nil];

    [[NSNotificationCenter defaultCenter] removeObserver:selfname:UIKeyboardWillShowNotification object:nil];

}

 

-(void)keyboardWillShow:(NSNotification *)note {

    CGRect keyboardRect = [[note.userInfoobjectForKey: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

Navigator模式

通常情况下,视图间的切换是通过调取to -pushViewController:animated:来实现的。随着过渡效果越来越复杂,你可以将这个任务指定给Navigator对象来完成。尤其是在同时支持iPhone和iPad的应用中,视图切换需要根据设备屏幕尺寸的不同而改变。

@protocolSKUserNavigator 

 

-(void)navigateToFollowersForUser:(SKUser *)user;

 

@end

 

@implementationSKiPhoneUserNavigator : NSObject

 

-(instancetype)initWithNavigationController:(UINavigationController*)navigationController {

    self = [super init];

    if (!self) return nil;

    _navigationController =navigationController;

   return self;

}

 

- (void)navigateToFollowersForUser:(SKUser*)user {

    SKFollowerListViewController *followerList= [[SKFollowerListViewController alloc] initWithUser:user];

    [self.navigationControllerpushViewController:followerList animated:YES];

}

 

@end
 @implementationSKiPadUserNavigator : NSObject

 

-(instancetype)initWithUserViewController:(SKUserViewController*)userViewController {

    self = [super init];

    if (!self) return nil;

    _userViewController = userViewController;

    return self;

}

 

-(void)navigateToFollowersForUser:(SKUser *)user {

    SKFollowerListViewController *followerList= [[SKFollowerListViewController alloc] initWithUser:user];

   self.userViewController.supplementalViewController = followerList;

}

总结

从历史来看,苹果的SDK只包含最小数量的原件,但是随着越来越多的API使用,我们经常会让View Controller的体量变得越来越大。将ViewController的职责指定给其他方式去完成,我们可以更好的控制View Controller的体积。

本文来源:


标签:

本站文章除注明转载外,均为本站原创或翻译。欢迎任何形式的转载,但请务必注明出处、不得修改原文相关链接,如果存在内容上的异议请邮件反馈至chenjj@pclwef.cn


为你推荐

  • 推荐视频
  • 推荐活动
  • 推荐产品
  • 推荐文章
  • 慧都慧问
扫码咨询


添加微信 立即咨询

电话咨询

客服热线
023-68661681

TOP