如何使用AutoLayout使UITableView标头的这个简单布局工作?

时间:2014-07-10 21:49:31

标签: ios objective-c uitableview uiview autolayout

我很惭愧发布这个,但我变得绝望了。

对于AutoLayout,我是一个菜鸟。这主要是因为我的应用程序中有近60个不同的屏幕。为什么改变什么工作?无论如何,我不使用XIB / NIB / Storyboard,因为任何时候我需要在应用程序范围内进行UI更改,我必须解决一些问题。相反,我有自己的一套UIViewController子类。其中一个只是像UITableViewController一样有一个UITableView。我可以在睡梦中制作出UITableViews。

我有一个新的屏幕,需要在表格视图的标题内有几个按钮。我正试图通过AutoLayout变得更好,所以我尽量使用它。没有进一步的叙述,这是一个非常简短的自成一体的例子。

#import "AppDelegate.h"

@interface TestViewController : UIViewController<UITableViewDataSource, UITableViewDelegate>
@property (nonatomic, strong) NSArray *rows;
@end

@implementation TestViewController
- (void) loadView {
    UITableView *tv = [[UITableView alloc] initWithFrame:[[UIScreen mainScreen] bounds] style:UITableViewStyleGrouped];
    [tv setAutoresizingMask:UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth];
    [tv setDelegate:self];
    [tv setDataSource:self];
    [self setView:tv];

    UIView *tableHeader = [[UIView alloc] init];
    [tableHeader setTranslatesAutoresizingMaskIntoConstraints:NO];
    [tv setTableHeaderView:tableHeader];

    UIButton *selectProfileButton = [UIButton buttonWithType:UIButtonTypeCustom];
    [selectProfileButton setTranslatesAutoresizingMaskIntoConstraints:NO];
    [selectProfileButton setShowsTouchWhenHighlighted:YES];
    [selectProfileButton setTitle:@"Select Profile..." forState:UIControlStateNormal];
    [tableHeader addSubview:selectProfileButton];

    UIButton *editProfileButton = [UIButton buttonWithType:UIButtonTypeCustom];
    [editProfileButton setTranslatesAutoresizingMaskIntoConstraints:NO];
    [editProfileButton setImage:[UIImage imageNamed:@"EditAccessoryIcon"] forState:UIControlStateNormal];
    [tableHeader addSubview:editProfileButton];


    UIEdgeInsets padding = UIEdgeInsetsMake(5, 10, 5, 5);

    // select button in NW corner
    [tableHeader addConstraint:[NSLayoutConstraint constraintWithItem:selectProfileButton attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual
                                                               toItem:tableHeader attribute:NSLayoutAttributeTop multiplier:1.0 constant:padding.top]];
    [tableHeader addConstraint:[NSLayoutConstraint constraintWithItem:selectProfileButton attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual
                                                               toItem:tableHeader attribute:NSLayoutAttributeLeft multiplier:1.0 constant:padding.left]];
    [tableHeader addConstraint:[NSLayoutConstraint constraintWithItem:selectProfileButton attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual
                                                               toItem:tableHeader attribute:NSLayoutAttributeBottom multiplier:1.0 constant:-padding.bottom]];

    // edit button in NE corner
    [tableHeader addConstraint:[NSLayoutConstraint constraintWithItem:editProfileButton attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual
                                                               toItem:tableHeader attribute:NSLayoutAttributeTop multiplier:1.0 constant:padding.top]];
    [tableHeader addConstraint:[NSLayoutConstraint constraintWithItem:editProfileButton attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual
                                                               toItem:tableHeader attribute:NSLayoutAttributeRight multiplier:1.0 constant:-padding.right]];
    [tableHeader addConstraint:[NSLayoutConstraint constraintWithItem:editProfileButton attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual
                                                               toItem:tableHeader attribute:NSLayoutAttributeBottom multiplier:1.0 constant:-padding.bottom]];

    // spacing between buttons
    [tableHeader addConstraint:[NSLayoutConstraint constraintWithItem:selectProfileButton attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual
                                                               toItem:editProfileButton attribute:NSLayoutAttributeLeft multiplier:1.0 constant:-10.0]];
}

- (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return [[self rows] count]; }

- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
    if (!cell) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cell"];
        [cell setSelectionStyle:UITableViewCellSelectionStyleNone];
    }

    [[cell textLabel] setText:[[self rows] objectAtIndex:[indexPath row]]];

    return cell;
}

@end

@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

    TestViewController *test = [[TestViewController alloc] init];
    [test setRows:@[@"1", @"2", @"3"]];
    [[self window] setRootViewController:test];

    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];
    return YES;
}
@end

不幸的是,在运行时我得到了这个:

2014-07-11 13:18:48.502 TestTableHeader[9478:60b] *** Assertion failure in -[UITableView layoutSublayersOfLayer:], /SourceCache/UIKit_Sim/UIKit-2935.137/UIView.m:8794
2014-07-11 13:18:48.505 TestTableHeader[9478:60b] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Auto Layout still required after executing -layoutSubviews. UITableView's implementation of -layoutSubviews needs to call super.'
*** First throw call stack:
(
    0   CoreFoundation                      0x017ed1e4 __exceptionPreprocess + 180
    1   libobjc.A.dylib                     0x0156c8e5 objc_exception_throw + 44
    2   CoreFoundation                      0x017ed048 +[NSException raise:format:arguments:] + 136
    3   Foundation                          0x0114c4de -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 116
    4   UIKit                               0x0029ba38 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 567
    5   libobjc.A.dylib                     0x0157e82b -[NSObject performSelector:withObject:] + 70
    6   QuartzCore                          0x03c5845a -[CALayer layoutSublayers] + 148
    7   QuartzCore                          0x03c4c244 _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 380
    8   QuartzCore                          0x03c583a5 -[CALayer layoutIfNeeded] + 160
    9   UIKit                               0x0035dae3 -[UIViewController window:setupWithInterfaceOrientation:] + 304
    10  UIKit                               0x00273aa7 -[UIWindow _setRotatableClient:toOrientation:updateStatusBar:duration:force:isRotating:] + 5212
    11  UIKit                               0x00272646 -[UIWindow _setRotatableClient:toOrientation:updateStatusBar:duration:force:] + 82
    12  UIKit                               0x00272518 -[UIWindow _setRotatableViewOrientation:updateStatusBar:duration:force:] + 117
    13  UIKit                               0x002725a0 -[UIWindow _setRotatableViewOrientation:duration:force:] + 67
    14  UIKit                               0x0027163a __57-[UIWindow _updateToInterfaceOrientation:duration:force:]_block_invoke + 120
    15  UIKit                               0x0027159c -[UIWindow _updateToInterfaceOrientation:duration:force:] + 400
    16  UIKit                               0x002722f3 -[UIWindow setAutorotates:forceUpdateInterfaceOrientation:] + 870
    17  UIKit                               0x002758e6 -[UIWindow setDelegate:] + 449
    18  UIKit                               0x0034fb77 -[UIViewController _tryBecomeRootViewControllerInWindow:] + 180
    19  UIKit                               0x0026b474 -[UIWindow addRootViewControllerViewIfPossible] + 591
    20  UIKit                               0x0026b5ef -[UIWindow _setHidden:forced:] + 312
    21  UIKit                               0x0026b86b -[UIWindow _orderFrontWithoutMakingKey] + 49
    22  UIKit                               0x002763c8 -[UIWindow makeKeyAndVisible] + 65
    23  TestTableHeader                     0x00002dfa -[AppDelegate application:didFinishLaunchingWithOptions:] + 890
    24  UIKit                               0x0022614f -[UIApplication _handleDelegateCallbacksWithOptions:isSuspended:restoreState:] + 309
    25  UIKit                               0x00226aa1 -[UIApplication _callInitializationDelegatesForURL:payload:suspended:] + 1810
    26  UIKit                               0x0022b667 -[UIApplication _runWithURL:payload:launchOrientation:statusBarStyle:statusBarHidden:] + 824
    27  UIKit                               0x0023ff92 -[UIApplication handleEvent:withNewEvent:] + 3517
    28  UIKit                               0x00240555 -[UIApplication sendEvent:] + 85
    29  UIKit                               0x0022d250 _UIApplicationHandleEvent + 683
    30  GraphicsServices                    0x037e2f02 _PurpleEventCallback + 776
    31  GraphicsServices                    0x037e2a0d PurpleEventCallback + 46
    32  CoreFoundation                      0x01768ca5 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 53
    33  CoreFoundation                      0x017689db __CFRunLoopDoSource1 + 523
    34  CoreFoundation                      0x0179368c __CFRunLoopRun + 2156
    35  CoreFoundation                      0x017929d3 CFRunLoopRunSpecific + 467
    36  CoreFoundation                      0x017927eb CFRunLoopRunInMode + 123
    37  UIKit                               0x0022ad9c -[UIApplication _run] + 840
    38  UIKit                               0x0022cf9b UIApplicationMain + 1225
    39  TestTableHeader                     0x00002fed main + 141
    40  libdyld.dylib                       0x01e34701 start + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException

从我读过的内容来看,这意味着我错过了一个约束。 UIButtons具有固有的大小,所以我只需要设置它们的位置。容器UIView将由UITableView调整大小,因此没有任何关系。 (我甚至尝试设置咯咯的高度 - 我没有傻笑。)

我搞砸了哪里?

谢谢!

更新了示例代码和堆栈,以提供简短的自包含示例。

1 个答案:

答案 0 :(得分:2)

我遇到了同样的问题。我认为这不是因缺少约束而引起的。这是因为UITableView显然没有正确处理自动布局的页眉/页脚。适用于我的解决方法是使用systemLayoutSizeFittingSize:方法设置框架:

//initialize tableHeader, add subviews and constraints
CGSize size = [tableHeader systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
tableHeader.frame = (CGRect){.origin = CGPointZero, .size = size};

self.tableView.tableHeaderView = tableHeader;

请注意,我没有translatesAutoresizingMaskIntoConstraints设置为NO

此外,您似乎还没有添加所有必需的限制。您只需定位按钮,但您的tableHeader不知道如何计算其自己的宽度和高度。您可能需要将selectProfileButton(或其他按钮的)底部等于tableHeader的底部,并在按钮之间添加(最小)水平间距。
另外,据我记忆,无论有什么限制,UITableView都会将tableHeader的宽度扩展到自己的宽度。