重构大方法以在 UIViewController 中创建自定义 UIView

Posted

技术标签:

【中文标题】重构大方法以在 UIViewController 中创建自定义 UIView【英文标题】:Refactoring large method to create custom UIView in UIViewController 【发布时间】:2014-02-14 17:35:18 【问题描述】:

我正在尝试在我的UIViewController 中正确重构一个方法。我对 Objective-C 相当陌生,因此试图了解这里最好的方法是什么。方法是在屏幕上创建一个自定义底栏。它是一个UIView,有多个子视图和自动布局。

目前,条形图的整个创建是在一种方法中,大约 300 行。理想情况下,我想将其移入子类,但需要了解如何正确完成。

这里是方法的开始,以了解它的作用:

-(void)setupBottomBarViewForOrientation:(UIInterfaceOrientation)forOrientation 
UIView *bottomBarInnerView = [UIView autolayoutView];
[self.bottomBarView addSubview:bottomBarInnerView];

NSDictionary *views = NSDictionaryOfVariableBindings(bottomBarInnerView);
NSDictionary *bottomBarMetrics = @@"minVPadding":@2.0;
[self.bottomBarView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-(minVPadding)-[bottomBarInnerView]-(minVPadding)-|" options:0 metrics:bottomBarMetrics views:views]];
[self.bottomBarView addConstraint:[NSLayoutConstraint constraintWithItem:bottomBarInnerView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.bottomBarView attribute:NSLayoutAttributeCenterX multiplier:1 constant:1]];

NSString *word = @"Word";
NSUInteger letterCount = [word length];

NSMutableArray *viewList = [[NSMutableArray alloc] init]; // Create an array to store the tiles within
NSDictionary *metrics = @@"letterTileMinHeight":@40.0,@"padding":@3.0,@"letterViewPadding":@5.0; // Constraint metrics
... [OTHER LOGIC] which create multiple subviews further UIViews and UILabels ...

将其移入UIView 子类的最佳途径是什么?那么自定义代码进入哪里(哪个方法)?还有如何从 VC 中调用并创建自动布局?

我了解子类化等,主要问题是如何创建自动布局约束。后来的一些自动布局约束从UIViewController 链接到self.view,那么这个视图是否应该被传递到创建横幅栏子视图的新方法中??

【问题讨论】:

【参考方案1】:

你绝对应该让它成为 UIView 的子类。传递配置它所需的任何内容。现在看起来像一本字典,所以你可以调用你的 init 方法类似

-(MyNewView*)initWithDictionary:(NSDictionary*)dict  

视图子类将为其中的事物设置自己的约束。如果使用它的视图控制器需要对整个子视图进行约束,那么它应该添加它们,但它不应该与其中的那些混淆。

这是一个子视图初始化方法的示例,它接受字符串和计数,创建两个标签来显示它们,并将它们添加到自身。您可以 layoutSubviews 或 autolayout 将它们定位在新视图中。

-(BottomBarInnerView *)initWithString:(NSString *)string count:(NSNumber*)count 
    self = [super initWithFrame:CGRectZero];
    UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 100, 50)];
    label.text = string;
    [self addSubview:label];

    UILabel *countLabel = [[UILabel alloc] initWithFrame:CGRectMake(100, 0, 20, 10)];
    countLabel.text = [count stringValue];
    [self addSubview:countLabel];

    return self;

由于视图使用 CGRectZero,因此您需要在创建它时调整它的大小,所以它是这样的:

BottomBarInnerView* bottomBarInnerView = [[BottomBarInnerView alloc] initWithString:@"Hi Mom" count:@(42)];
bottomBarInnerView.frame = CGRectMake(0, 200, 100, 50);
[self.view addSubview:bottomBarInnerView];

或者,您可以为其添加约束,并让自动布局为您设置框架。

【讨论】:

好的,我是否创建UIView 的子视图,然后在 init 方法中调用 initWithDictionary 或我最终使用的任何内容?我已经看到它使用 layoutSubViews 等完成,我应该在实际的子视图类中使用什么方法/方法? 在您的 initWithDictionary 中您将执行 self = [super init],这会将您的对象变成 UIView。在那之后,做任何你需要做的事情来让它成为你的。添加子视图,当然,然后在 layoutSubviews 中布局它们,或者使用自动布局,这似乎是你最初想要做的。 嘿@jsd。这里的实际要求是从问题中生成bottomBarInnerView。不需要字典,因为它用于该视图的约束。我需要将UIView 子类化为bottomBarInnerView。因此,字符串、计数等将被传递到相关的新 init 方法和代码 [AFTER] 上面的大块中。无论如何,问题是我如何子类化UIView 并正确配置它。 IE。我要继承UIView,然后调用 alloc/init - 然后将所有代码放在后面。还是我创建一个自定义的initWithString 方法然后调用... ...alloc/initWithString ?然后在子类的那个方法中它调用super self?抱歉,整个调用子类和子类中的自定义 init 方法导致了混乱。谢谢 我已经编辑了我的答案以包含一个代码示例,因为这个评论太长了。

以上是关于重构大方法以在 UIViewController 中创建自定义 UIView的主要内容,如果未能解决你的问题,请参考以下文章

将 UITableViewController 子类重构为 UIViewController 子类

重构此函数以在 if 语句 Javascript 内部和外部一致地使用“return”

动态子类化 UIViewController 的错误

重构心法——拆分大对象

重构心法——分解大函数

UIViewController 自定义过渡风格(由内而外呈现)