contentView 在 iOS 6 UITableViewCell 原型单元格中没有缩进

Posted

技术标签:

【中文标题】contentView 在 iOS 6 UITableViewCell 原型单元格中没有缩进【英文标题】:contentView not indenting in iOS 6 UITableViewCell prototype cell 【发布时间】:2012-09-26 11:00:00 【问题描述】:

我正在使用情节提要中的原型单元格配置自定义 UITableViewCell。但是,所有UILabels(和其他UI 元素)似乎都没有添加到单元格的contentView,而是直接添加到UITableViewCell 视图中。当单元格进入编辑模式时,这会产生问题,因为内容不会自动移动/缩进(如果它们在 contentView 内,它会这样做)。

在使用 Interface Builder/Storyboard/prototype 单元格布置单元格时,有什么方法可以将 UI 元素添加到 contentView?我发现的唯一方法是在代码中创建所有内容并使用 [cell.contentView addSubView:labelOne] 这不是很好,因为以图形方式布局单元格要容易得多。

【问题讨论】:

你确定吗?上次我在 nib 中布置了一个单元格,虽然看起来我没有向内容视图添加子视图,但在调试器中运行时,所有内容都在内容视图中。如果您还没有,值得在调试器中验证... 谢谢卡尔。你是对的 - 所有子视图都添加到 contentView 中。该问题与 ios 6 自动布局有关。我已经提供了一个答案,其中概述了问题是如何解决的。 【参考方案1】:

进一步调查(查看单元格的子视图层次结构)Interface Builder 确实将子视图放置在单元格的contentView 中,只是看起来不像。

问题的根本原因是 iOS 6 自动布局。当单元格被置于编辑模式(并缩进)时,contentView 也被缩进,因此contentView 内的所有子视图都将由于位于contentView 内而移动(缩进)。但是,Interface Builder 应用的所有自动布局约束似乎都与UITableViewCell 本身相关,而不是contentView。这意味着即使 contentView 缩进,其中包含的子视图也不 - 约束负责。

例如,当我将UILabel 放入单元格中(并将其放置在距离单元格左侧 10 个点的位置)时,IB 自动应用了约束“水平空间 (10)”。然而,这个约束是相对于UITableViewCell 而不是contentView。这意味着当单元格被缩进并且contentView 移动时,标签会保持原样,因为它符合从UITableViewCell 左侧保持10 个点的约束。

不幸的是(据我所知)没有办法从 IB 本身中删除这些 IB 创建的约束,所以这就是我解决问题的方法。

在单元格的UITableViewCell 子类中,我为名为@9​​87654336@ 的约束创建了一个IBOutlet。标签本身还需要一个IBOutlet,我称之为cellLabel。然后我实现了-awakeFromNib 方法,如下所示:

- (void)awakeFromNib 

    // -------------------------------------------------------------------
    // We need to create our own constraint which is effective against the
    // contentView, so the UI elements indent when the cell is put into
    // editing mode
    // -------------------------------------------------------------------

    // Remove the IB added horizontal constraint, as that's effective
    // against the cell not the contentView
    [self removeConstraint:self.cellLabelHSpaceConstraint];

    // Create a dictionary to represent the view being positioned
    NSDictionary *labelViewDictionary = NSDictionaryOfVariableBindings(_cellLabel);   

    // Create the new constraint
    NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"|-10-[_cellLabel]" options:0 metrics:nil views:labelViewDictionary];

    // Add the constraint against the contentView
    [self.contentView addConstraints:constraints];


综上所述,以上将删除 IB 自动添加的水平间距约束(对UITableViewCell 而非contentView 有效),然后我们定义并将自己的约束添加到contentView

在我的例子中,单元格中的所有其他UILabels 都是根据cellLabel 的位置定位的,所以当我修复这个元素的约束/定位时,所有其他元素都遵循并正确定位。但是,如果您有更复杂的布局,那么您可能还需要对其他子视图执行此操作。

【讨论】:

感谢您发布详细的解决方法。有同样的问题! 您可以通过在界面生成器中关闭自动布局来解决此问题,无论是整个情节提要还是仅包含您的单元格的笔尖。有关详细信息,请参见此处的答案:***.com/questions/12833176/… 是的,没错。然而,自动布局对于方向变化非常有用(特别是考虑到我设计的复杂单元格)。在我的用例中,此变通办法在保持启用自动布局的同时(与在设备旋转时必须手动完全重新布局单元格相比)的开销足以证明它的合理性。但是,希望这将在未来的版本中得到解决... 我已经查看了 IB 生成的代码,你的权利,所有的约束都用 TebleViewCell 而不是 contentView 表示(即使 contentView 存在于 XML 中但不能从 IB 访问用户界面)。可以通过在 IB 中表示相对于 TableViewCell 右侧的所有约束并将约束优先级小于 1000 的约束从您的解决方案 (cellLabelHSpaceConstraint) 中删除一个出口。使用这种方法,您只需要一个出口和不需要删除原始约束。 +1 感谢您发布此内容 - 我刚刚使用它,效果很好!这似乎是 IB 处理 UITableViewCell 自定义内容约束的方式中的一个错误。【参考方案2】:

如前所述,XCode 的 Interface Builder 隐藏了 UITableViewCell 的 contentView。实际上,所有添加到 UITableViewCell 的 UI 元素实际上都是 contentView 的子视图。

目前,IB 并没有为布局约束做同样的事情,这意味着它们都在 UITableViewCell 级别表示。

解决方法是在子类的 awakeFromNib 中将所有 NSAutoLayoutConstrains 从 UITableViewCell 移动到它的 contentView 并以 contentView 的形式表达它们:

-(void)awakeFromNib
  [super awakeFromNib];
  for(NSLayoutConstraint *cellConstraint in self.constraints)
    [self removeConstraint:cellConstraint];
    id firstItem = cellConstraint.firstItem == self ? self.contentView : cellConstraint.firstItem;
    id seccondItem = cellConstraint.secondItem == self ? self.contentView : cellConstraint.secondItem;
    NSLayoutConstraint* contentViewConstraint =
    [NSLayoutConstraint constraintWithItem:firstItem
                                 attribute:cellConstraint.firstAttribute
                                 relatedBy:cellConstraint.relation
                                    toItem:seccondItem
                                 attribute:cellConstraint.secondAttribute
                                multiplier:cellConstraint.multiplier
                                  constant:cellConstraint.constant];
    [self.contentView addConstraint:contentViewConstraint];
  

【讨论】:

这很棒!正在使用分组表视图,并且必须使所有内容都与界面构建器中的单元格相关,而不是与它的视觉外观相关,但它可以让它正确缩进和剪辑! +1 将此添加到我的基础UITableViewCell,这样我就不必再处理这个问题了。 该解决方案对我有用,但我不得不在别处进行一项更改。而不是[cell systemLayoutSizeFittingSize:UILayoutFittingCompressedSize]; 我打电话给[cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize]; 完美!这正是 IB 应该做的。【参考方案3】:

这是一个子类,基于其他答案的想法,我将基于我的自定义单元格:

@interface FixedTableViewCell ()

- (void)initFixedTableViewCell;

@end

@interface FixedTableViewCell : UITableViewCell

@end

@implementation FixedTableViewCell

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier 
    if (nil != (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier])) 
        [self initFixedTableViewCell];
    
    return self;


- (void)awakeFromNib 
    [super awakeFromNib];

    [self initFixedTableViewCell];


- (void)initFixedTableViewCell 
    for (NSInteger i = self.constraints.count - 1; i >= 0; i--) 
        NSLayoutConstraint *constraint = [self.constraints objectAtIndex:i];

        id firstItem = constraint.firstItem;
        id secondItem = constraint.secondItem;

        BOOL shouldMoveToContentView = YES;

        if ([firstItem isDescendantOfView:self.contentView]) 
            if (NO == [secondItem isDescendantOfView:self.contentView]) 
                secondItem = self.contentView;
            
        
        else if ([secondItem isDescendantOfView:self.contentView]) 
            if (NO == [firstItem isDescendantOfView:self.contentView]) 
                firstItem = self.contentView;
            
        
        else 
            shouldMoveToContentView = NO;
        

        if (shouldMoveToContentView) 
            [self removeConstraint:constraint];
            NSLayoutConstraint *contentViewConstraint = [NSLayoutConstraint constraintWithItem:firstItem
                                                                                     attribute:constraint.firstAttribute
                                                                                     relatedBy:constraint.relation
                                                                                        toItem:secondItem
                                                                                     attribute:constraint.secondAttribute
                                                                                    multiplier:constraint.multiplier
                                                                                      constant:constraint.constant];
            [self.contentView addConstraint:contentViewConstraint];
        
    


@end

【讨论】:

在我看来,从 initWithStyle:reuseIdentifier: 调用 initFixedTableViewCell 是没有用的,因为它是一个 Interface Builder 错误,只有从 nib 文件加载 cel 时才会发生。希望在代码中,不应该重现 Interface Builder 的错误 ;-) 我也不明白为什么要在约束列表中检查后代。 这对我来说效果很好,但您也需要保留约束优先级:“contentViewConstraint.priority = constraint.priority”【参考方案4】:

子类化的替代方法是修改 cellForRowAtIndexPath 中的约束。

将单元格的所有内容嵌入到容器视图中。然后将前导和尾随约束指向 cell.contentView 而不是表格视图单元格。

  UIView *containerView = [cell viewWithTag:999];
  UIView *contentView = [cell contentView];

  //remove existing leading and trailing constraints
  for(NSLayoutConstraint *c in [cell constraints])
    if(c.firstItem==containerView && (c.firstAttribute==NSLayoutAttributeLeading || c.firstAttribute==NSLayoutAttributeTrailing))
      [cell removeConstraint:c];
    
  

  NSLayoutConstraint *trailing = [NSLayoutConstraint
                                 constraintWithItem:containerView
                                 attribute:NSLayoutAttributeTrailing
                                 relatedBy:NSLayoutRelationEqual
                                 toItem:contentView
                                 attribute:NSLayoutAttributeTrailing
                                 multiplier:1
                                 constant:0];

  NSLayoutConstraint *leading = [NSLayoutConstraint
                                 constraintWithItem:containerView
                                 attribute:NSLayoutAttributeLeading
                                 relatedBy:NSLayoutRelationEqual
                                 toItem:contentView
                                 attribute:NSLayoutAttributeLeading
                                 multiplier:1
                                 constant:0];

  [cell addConstraint:trailing];
  [cell addConstraint:leading];

【讨论】:

【参考方案5】:

我认为这已在 iOS 7 beta 3 中得到修复,因此从那时起,变通方法就变得不必要了(但可能是无害的,因为在大多数情况下它们会变成空操作)。

【讨论】:

【参考方案6】:

基于 Skoota 的代码(我是初学者,不太了解你做了什么,但工作出色)我的建议是将你所有的东西放在一个边缘到边缘的容器视图中并添加以下内容:

在单元格的头文件中,我有以下 IBOutlets:

@property (weak, nonatomic) IBOutlet UIView *container;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *leftConstrain;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *rightConstrain;

在实现文件中,我在 awakeFromNib 中有以下内容:

// Remove the IB added horizontal constraint, as that's effective gainst the cell not the contentView
[self removeConstraint:self.leftConstrain];
[self removeConstraint:self.rightConstrain];

// Create a dictionary to represent the view being positioned
NSDictionary *containerViewDictionary = NSDictionaryOfVariableBindings(_container);

// Create the new left constraint (0 spacing because of the edge-to-edge view 'container')
NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"|-0-[_container]" options:0 metrics:nil views:containerViewDictionary];
// Add the left constraint against the contentView
[self.contentView addConstraints:constraints];

// Create the new constraint right (will fix the 'Delete' button as well)
constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"[_container]-0-|" options:0 metrics:nil views:containerViewDictionary];
// Add the right constraint against the contentView
[self.contentView addConstraints:constraints];

同样,Skoota 使上述一切成为可能。谢谢!!!所有学分都归他所有。

【讨论】:

以上是关于contentView 在 iOS 6 UITableViewCell 原型单元格中没有缩进的主要内容,如果未能解决你的问题,请参考以下文章

如何跳转到iOS开发中另一个UITab下的其他UINavigationController?

ContentViews Subview 在 UIScrollView iOS 中没有动态改变高度

是否可以在 Xamarin Forms ContentView 中嵌入 Android.VideoView

我们如何为多个 UITab 使用相同的 WKWebView

带有scrollView的iOS页面随机无法正确加载contentView

如何在ios中使用contentview组件获取单元格索引值[重复]