ios - UITableView 部分标题的自动布局问题

标签 ios objective-c uitableview autolayout

我正在使用 UITableViewController。我有一个项目表,如果他进入编辑更多,用户可以删除这些项目。当他进入编辑模式时,我想显示一个标题,提供删除所有项目的选项。同时,它应该显示一个标签,提供有关正在使用多少空间的信息。如果设备进入横向模式,我希望它自动调整大小。据我所知,我需要使用自动布局来执行此操作。

我很想在 Storyboard 中设计的 UIView 中设置标题,但 Storyboard 只允许 View Controller ,而不是 View 。我知道我可以用 XIB 文件保存它,但如果可以的话,我宁愿避免这种情况。

首先,我覆盖了 editing 属性,这样我就可以在编辑模式下重绘表格部分。

- (void)setEditing:(BOOL)editing animated:(BOOL)animated
{
    [super setEditing:editing animated:animated];
    NSIndexSet *set = [NSIndexSet indexSetWithIndex:0];
    [self.tableView reloadSections:set withRowAnimation:UITableViewRowAnimationAutomatic];
}

我使用此代码在适当的时候插入节标题:

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
    if (self.isEditing)
        return [self headerView];
    else
        return nil;
}

- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
    if (self.isEditing)
        return [self headerView].frame.size.height;
    else
        return 0;
}

魔法发生在 - headerView 方法中。它返回一个 UIView *,必要时从缓存中获取它。它添加按钮和标签,然后放入约束。我在 Storyboard 中使用了这些相同的约束,但没有遇到任何问题。

- (UIView *)headerView
{
    if (headerView)
        return headerView;

    float w = [[UIScreen mainScreen] bounds].size.width;

    UIButton *deleteAllButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    [deleteAllButton setTitle:@"Delete All" forState:UIControlStateNormal];
    CGRect deleteAllButtonFrame = CGRectMake(8.0, 8.0, 30.0, 30);   // The autolayout should resize this.
    [deleteAllButton setFrame:deleteAllButtonFrame];
    deleteAllButton.translatesAutoresizingMaskIntoConstraints = NO;
    [deleteAllButton setContentHuggingPriority:252 forAxis:UILayoutConstraintAxisHorizontal];
    [deleteAllButton setContentCompressionResistancePriority:751 forAxis:UILayoutConstraintAxisHorizontal];

    CGRect textFrame = CGRectMake(47.0, 8.0, 30.0, 30); // The autolayout should resize this.
    UILabel *currSizeText = [[UILabel alloc] initWithFrame:textFrame];
    currSizeText.text = @"You have a lot of text here telling you that you have stuff to delete";
    currSizeText.translatesAutoresizingMaskIntoConstraints = NO;
    currSizeText.adjustsFontSizeToFitWidth = YES;

    CGRect headerViewFrame = CGRectMake(0, 0, w, 48);
    headerView = [[UIView alloc] initWithFrame:headerViewFrame];
    //headerView.autoresizingMask = UIViewAutoresizingNone;//UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
    //headerView.translatesAutoresizingMaskIntoConstraints = NO;
    [headerView addSubview:deleteAllButton];
    [headerView addSubview:currSizeText];

    NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(deleteAllButton, currSizeText);

    [headerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"|-[deleteAllButton]-[currSizeText]-|"
                                                                       options:0
                                                                       metrics:nil
                                                                         views:viewsDictionary]];

    [headerView addConstraint:[NSLayoutConstraint constraintWithItem:deleteAllButton
                                      attribute:NSLayoutAttributeHeight
                                      relatedBy:NSLayoutRelationEqual
                                         toItem:headerView
                                      attribute:NSLayoutAttributeHeight
                                     multiplier:0.5
                                       constant:0]];
    [headerView addConstraint:[NSLayoutConstraint constraintWithItem:currSizeText
                                                           attribute:NSLayoutAttributeHeight
                                                           relatedBy:NSLayoutRelationEqual
                                                              toItem:headerView
                                                           attribute:NSLayoutAttributeHeight
                                                          multiplier:0.5
                                                            constant:0]];
    return headerView;
}

现在,一切都运行良好。按钮保持恒定大小(因为拥抱和压缩阻力高于标签)并且标签将更改其文本以适应可用空间。当我旋转设备时它会调整大小。垂直居中似乎不在标签上,但我现在愿意忽略它。

但是,当我第一次设置节标题时,我收到了一个烦人的自动布局警告。

2014-02-07 11:25:19.770 ErikApp[10704:70b] Unable to simultaneously satisfy constraints.
    Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) 
(
    "<NSLayoutConstraint:0xb9a4ad0 H:|-(NSSpace(20))-[UIButton:0xb99e220]   (Names: '|':UIView:0xb9a4680 )>",
    "<NSLayoutConstraint:0xb9a4bf0 H:[UIButton:0xb99e220]-(NSSpace(8))-[UILabel:0xb99f530]>",
    "<NSLayoutConstraint:0xb9a4c20 H:[UILabel:0xb99f530]-(NSSpace(20))-|   (Names: '|':UIView:0xb9a4680 )>",
    "<NSAutoresizingMaskLayoutConstraint:0xa2d1680 h=--& v=--& H:[UIView:0xb9a4680(0)]>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0xb9a4bf0 H:[UIButton:0xb99e220]-(NSSpace(8))-[UILabel:0xb99f530]>

Break on objc_exception_throw to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.

我的第一个想法是将返回的 UIView 属性 translatesAutoresizingMaskIntoConstraints 更改为 NO。当我这样做时,我得到的是崩溃而不是警告。不完全是改进。

2014-02-07 10:49:13.041 ErikApp[10597:70b] *** Assertion failure in -[UITableView layoutSublayersOfLayer:], /SourceCache/UIKit_Sim/UIKit-2903.23/UIView.m:8540
2014-02-07 10:49:13.383 ErikApp[10597:70b] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Auto Layout still required after executing -layoutSubviews. UITableView's implementation of -layoutSubviews needs to call super.'

有没有人建议如何消除警告?

最佳答案

似乎当您的部分正在重新加载时,UITableView 有时会同时引用旧部分标题和新部分标题。如果是相同的 View ,则会出现一些问题。因此,您必须始终提供与 tableView:viewForHeaderInSection: 方法不同的 View 。

有时在节标题中显示单个实例非常有用。为此,每次系统要求您提供节标题时,您都需要创建一个新 View ,并将您的自定义 View 放入其中,并适本地配置约束。这是一个例子:

@property (strong, nonatomic) UIView *headerContentView;

- (void)viewDidLoad {
    // Create the view, which is to be presented inside the section header
    self.headerContentView = [self loadHeaderContentView];
    // Note that we have to set the following property to NO to prevent the unsatisfiable constraints
    self.headerContentView.translatesAutoresizingMaskIntoConstraints = NO;
}

- (UIView *)loadHeaderContentView {
    // Here you instantiate your custom view from a nib
    // or create it programmatically. Speaking in terms
    // of the OP, it should look like the following. (Note:
    // I have removed all the frame-related code as your are
    // not supposed to deal with frames directly with auto layout.
    // I have also removed the line setting translatesAutoresizingMaskIntoConstraints property
    // to NO of the headerContentView object as we do it explicitly in viewDidLoad.
    UIButton *deleteAllButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    [deleteAllButton setTitle:@"Delete All" forState:UIControlStateNormal];
    deleteAllButton.translatesAutoresizingMaskIntoConstraints = NO;
    [deleteAllButton setContentHuggingPriority:252 forAxis:UILayoutConstraintAxisHorizontal];
    [deleteAllButton setContentCompressionResistancePriority:751 forAxis:UILayoutConstraintAxisHorizontal];

    UILabel *currSizeText = [[UILabel alloc] init];
    currSizeText.text = @"You have a lot of text here telling you that you have stuff to delete";
    currSizeText.translatesAutoresizingMaskIntoConstraints = NO;
    currSizeText.adjustsFontSizeToFitWidth = YES;

    UIView *headerContentView = [[UIView alloc] init];
    [headerContentView addSubview:deleteAllButton];
    [headerContentView addSubview:currSizeText];

    NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(deleteAllButton, currSizeText);

    // In the original post you used to have an ambigious layout
    // as the Y position of neither button nor label was set.
    // Note passing NSLayoutFormatAlignAllCenterY as an option
    [headerContentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"|-[deleteAllButton]-[currSizeText]-|"
                                                                              options:NSLayoutFormatAlignAllCenterY
                                                                              metrics:nil
                                                                                views:viewsDictionary]];
    [headerContentView addConstraint:[NSLayoutConstraint constraintWithItem:deleteAllButton
                                                                  attribute:NSLayoutAttributeCenterY
                                                                  relatedBy:NSLayoutRelationEqual
                                                                     toItem:headerContentView
                                                                  attribute:NSLayoutAttributeCenterY
                                                                 multiplier:1
                                                                   constant:0]];
    // Here setting the heights of the subviews
    [headerContentView addConstraint:[NSLayoutConstraint constraintWithItem:deleteAllButton
                                                           attribute:NSLayoutAttributeHeight
                                                           relatedBy:NSLayoutRelationEqual
                                                              toItem:headerContentView
                                                           attribute:NSLayoutAttributeHeight
                                                          multiplier:0.5
                                                            constant:0]];
    [headerContentView addConstraint:[NSLayoutConstraint constraintWithItem:currSizeText
                                                           attribute:NSLayoutAttributeHeight
                                                           relatedBy:NSLayoutRelationEqual
                                                              toItem:headerContentView
                                                           attribute:NSLayoutAttributeHeight
                                                          multiplier:0.5
                                                            constant:0]];
    return headerContentView;
}

- (UIView *)headerView {
    UIView *headerView = [[UIView alloc] init];
    [headerView addSubview:self.headerContentView];

    NSDictionary *views = @{@"headerContentView" : self.headerContentView};
    NSArray *hConstraints = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[headerContentView]|" options:0 metrics:nil views:views];
    NSArray *vConstraints = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|[headerContentView]|" options:0 metrics:nil views:views];
    [headerView addConstraints:hConstraints];
    [headerView addConstraints:vConstraints];

    return headerView;
}

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
    if (self.isEditing)
        return [self headerView];
    return nil;      
}

- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
    // You need to return a concrete value here
    // and not the current height of the header.
    if (self.isEditing)
        return 48;
    return 0;
}

关于ios - UITableView 部分标题的自动布局问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21635193/

相关文章:

objective-c - UITableViewCell 中的 UITextfield 退出 FirstResnponder

ios - Swift 3-使用自定义对象声明NSMutableArray数据类型

ios - 辞去 UITextField 的第一响应者时,NSParagraphStyle 发生了变化

ios - 如何更改单个表格部分的分隔线颜色?

ios - 新 Xcode beta 新问题 : MKGeodesicPolyline

ios - 更改 View 会导致屏幕上出现内容

ios - 在另存为 PNG 之前将新创建的 iOS 图像旋转 90 度

ios - 如何使用 google Api 查找我周围的地方

ios - TableView 折叠/展开部分

swift - UIRefreshControl 故障与自定义 TableViewCell 相结合