iOS - 查找视图的顶部约束?

Posted

技术标签:

【中文标题】iOS - 查找视图的顶部约束?【英文标题】:iOS - Find top constraint for a view? 【发布时间】:2014-11-09 21:39:35 【问题描述】:

我试图在代码中找到视图的顶部约束。 故事板中添加了顶部约束,我不想使用 IBOutlet。

在以下代码中记录 firstAttribute 的值似乎总是返回 NSLayoutAttributeHeight 类型的约束。知道如何在代码中可靠地找到视图的最高约束吗?

NSLayoutConstraint *topConstraint;

for (NSLayoutConstraint *constraint in self.constraints) 
    if (constraint.firstAttribute == NSLayoutAttributeTop) 
        topConstraint = constraint;
        break;
    

【问题讨论】:

【参考方案1】:

您应该遍历self.superview.constraints,而不是遍历self.constraints

self.constraints 仅包含与视图相关的约束(例如高度和宽度约束)。

这是一个可能看起来像这样的代码示例:

- (void)awakeFromNib

  [super awakeFromNib];

  if (!self.topConstraint) 
    [self findTopConstraint];
  


- (void)findTopConstraint

  for (NSLayoutConstraint *constraint in self.superview.constraints) 
    if ([self isTopConstraint:constraint]) 
      self.topConstraint = constraint;
      break;
    
  


- (BOOL)isTopConstraint:(NSLayoutConstraint *)constraint

  return  [self firstItemMatchesTopConstraint:constraint] ||
          [self secondItemMatchesTopConstraint:constraint];


- (BOOL)firstItemMatchesTopConstraint:(NSLayoutConstraint *)constraint

  return constraint.firstItem == self && constraint.firstAttribute == NSLayoutAttributeTop;


- (BOOL)secondItemMatchesTopConstraint:(NSLayoutConstraint *)constraint

  return constraint.secondItem == self && constraint.secondAttribute == NSLayoutAttributeTop;

【讨论】:

感谢通过父母的约束进行枚举 您可以通过将 findTopConstraint 更改为使用 UIView 方法 constraintsAffectingLayoutForAxis: 返回的数组来使其更准确和更短。这消除了正交轴上的约束。 @uchuugaka Apple 文档中的这个声明让我暂停使用constraintsAffectingLayoutForAxis:This method should only be used for debugging constraint-based layout. No application should ship with calls to this method as part of its operation. AFAIK,但是通过 constraints 数组枚举没有任何问题。 没注意到。很好地找到了文档说明。【参考方案2】:

我通常在 IB 中设置一个所需约束的identifier,然后在这样的代码中找到它(Swift):

if let index = constraints.index(where:  $0.identifier == "checkmarkLeftMargin" ) 
    checkmarkImageViewLeftMargin = constraints[index]

或@Tim Vermeulen

checkmarkImageViewLeftMargin = constraints.first  $0.identifier == "checkmarkLeftMargin" 

【讨论】:

if let constraint = constraints.filter $0.identifier == "checkmarkLeftMargin" .first :) @SeanDev 一旦必要的约束源,循环就会中断。在第一个示例中,所有约束都使用指定的标识符进行搜索。当然,在实际条件下,它不会花费太多时间,但仍然如此。 @aryaxt 有first(where:) 如果您在界面生成器中完成所有这些,您还可以设置一个 Outlet。【参考方案3】:

使用 swift 和 UIView 扩展

extension UIView 
    func findConstraint(layoutAttribute: NSLayoutAttribute) -> NSLayoutConstraint? 
        if let constraints = superview?.constraints 
            for constraint in constraints where itemMatch(constraint: constraint, layoutAttribute: layoutAttribute) 
                return constraint
            
        
        return nil
    

    func itemMatch(constraint: NSLayoutConstraint, layoutAttribute: NSLayoutAttribute) -> Bool 
        if let firstItem = constraint.firstItem as? UIView, let secondItem = constraint.secondItem as? UIView 
            let firstItemMatch = firstItem == self && constraint.firstAttribute == layoutAttribute
            let secondItemMatch = secondItem == self && constraint.secondAttribute == layoutAttribute
            return firstItemMatch || secondItemMatch
        
        return false
    

【讨论】:

【参考方案4】:

基于@Igor answer,我更改了位itemMatch 方法以考虑第一项或第二项不是UIView 的情况。例如,当将 UIView 顶部约束到安全区域顶部时。

extension UIView 
    func findConstraint(layoutAttribute: NSLayoutConstraint.Attribute) -> NSLayoutConstraint? 
        if let constraints = superview?.constraints 
            for constraint in constraints where itemMatch(constraint: constraint, layoutAttribute: layoutAttribute) 
                return constraint
            
        
        return nil
    

    func itemMatch(constraint: NSLayoutConstraint, layoutAttribute: NSLayoutConstraint.Attribute) -> Bool 
        let firstItemMatch = constraint.firstItem as? UIView == self && constraint.firstAttribute == layoutAttribute
        let secondItemMatch = constraint.secondItem as? UIView == self && constraint.secondAttribute == layoutAttribute
        return firstItemMatch || secondItemMatch
    

【讨论】:

【参考方案5】:

在 Xcode 的检查器中设置标识符。这就是它的用途。你给它命名。 如果这还不够,您可以创建 IBOutlet。

【讨论】:

这并不适用于所有情况。例如,如果您正在开发一个打算供其他开发人员使用/子类化/等的库,您可能希望他们必须显式地将 IBOutlet 设置为顶部约束。跨度> 为什么不呢?如果你不打算这样做,那么他们必须这样做。否则,您使用标识符命名它。但是明确的属性是清晰易懂的。 我不想使用标识符,否则我会使用 IBOUTlet。我希望它能够动态工作 动态意味着创建易于识别的事物。如今,UIKit 和 AppKit 到处都有标识符属性是有原因的。如果没有这个或插座,那么您只能通过查看影响给定视图的特定方向布局的每个约束的属性来猜测。 使用标识符或插座不是动态的。使用代码并通过约束不是猜测工作,看看上面提供的示例非常可靠。使用标识符,您必须记住标识符名称应该是什么是猜测工作。在为具有特定标识符(非动态)的特定案例编写代码时使用标识符【参考方案6】:

我用 Swift 写了一个小扩展:

extension UIButton 
    var topConstraints: [NSLayoutConstraint]? 
        return self.constraints.filter(  ($0.firstItem as? UIButton == self && $0.firstAttribute == .top) || ($0.secondItem as? UIButton == self && $0.secondAttribute == .top) )
    

【讨论】:

【参考方案7】:

这是一种基于@Igor 方法的单行扩展方法:

extension UIView

    func constraint(for layoutAttribute: NSLayoutConstraint.Attribute) -> NSLayoutConstraint? 
        return superview?.constraints.first  itemMatch(constraint: $0, layoutAttribute: layoutAttribute) 
    

    private func itemMatch(constraint: NSLayoutConstraint, layoutAttribute: NSLayoutConstraint.Attribute) -> Bool 
        if let firstItem = constraint.firstItem as? UIView, let secondItem = constraint.secondItem as? UIView 
            let firstItemMatch = firstItem == self && constraint.firstAttribute == layoutAttribute
            let secondItemMatch = secondItem == self && constraint.secondAttribute == layoutAttribute
            return firstItemMatch || secondItemMatch
        
        return false
    


[我还制作了方法签名样式以匹配 Swift 3...5 样式,例如:

constraint(for:) 而不是findConstraint(layoutAttribute:)。 ]

【讨论】:

以上是关于iOS - 查找视图的顶部约束?的主要内容,如果未能解决你的问题,请参考以下文章

随着 iOS 7 的视图控制器是全屏的,是不是可以在视图的最顶部设置约束,而不仅仅是导航栏?

ios xcode 电容器故事板约束以停止状态栏挂在 web 视图的顶部

顶部安全区域约束动画

iOS 约束:如何给 UILabel 高度约束优先级

iOS - 顶部布局指南高度问题

iOS 如何设置约束以适应设备屏幕?