CALayer IOS上的自动布局约束

时间:2014-06-19 09:30:14

标签: ios autolayout calayer

您好我正在开发iPhone应用程序,其中我尝试为edittext设置一个边框。我是按照以下方式做到的:

 int borderWidth = 1;
CALayer *leftBorder = [CALayer layer];

leftBorder.borderColor = [UIColor whiteColor].CGColor;
leftBorder.borderWidth = borderWidth;

leftBorder.frame = CGRectMake(0, textField.frame.size.height - borderWidth, textField
                              .frame.size.width, borderWidth);
[textField.layer addSublayer:leftBorder];

我对IB中的edittext设置了一些约束,这样当我旋转设备时,它会根据它调整文本字段的宽度。我的问题是调整edittext的宽度而不是调整我为编辑文本设置的CALayer的宽度。所以我想我必须为我的CALayer项目设置一些约束。但我不知道该怎么做。有人知道吗?需要帮忙。谢谢。

9 个答案:

答案 0 :(得分:116)

整个自动化业务是特定于视图的。图层不会自动调整大小。

你需要做的 - 在代码中 - 是自己调整图层大小

e.g。

在viewController中你会做什么

- (void) viewDidLayoutSubviews {
  [super viewDidLayoutSubviews]; //if you want superclass's behaviour... 
  // resize your layers based on the view's new frame
  self.editViewBorderLayer.frame = self.editView.bounds;
}

或在自定义UIView中,您可以使用

- (void)layoutSubviews {
  [super layoutSubviews]; //if you want superclass's behaviour...  (and lay outing of children)
  // resize your layers based on the view's new frame
  layer.frame = self.bounds;
}

答案 1 :(得分:7)

查看此解决方案。 使用KVO,路径" YourView.bounds"如下所示。

self.addObserver(self, forKeyPath: "YourView.bounds", options: .New, context: nil)

然后如下所示处理它。

override func observeValueForKeyPath(keyPath: String, ofObject object: AnyObject, change: [NSObject : AnyObject], context: UnsafeMutablePointer<Void>) {
        if (keyPath == "YourView.bounds") {
            YourLayer.frame = YourView.bounds
            return
        }
        super.observeValueForKeyPath(keyPath, ofObject: object, change: change, context: context)
    }

有关详细信息,请参阅以下链接。

https://theswiftdev.com/2015/03/28/auto-layout-with-layers/

答案 2 :(得分:5)

我实现了自定义视图的方法layoutSubviews;在这个方法中,我只是更新每个子层的帧以匹配我的子视图层的当前边界。

-(void)layoutSubviews{
      sublayer1.frame = self.layer.bounds;
}

答案 3 :(得分:3)

根据this回答,layoutSubviews在所有需要的情况下都不会被调用。我发现这种委托方法更有效:

- (instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if(self != nil) {
        [self.layer addSublayer:self.mySublayer];
    }
    return self;
}

- (void)layoutSublayersOfLayer:(CALayer*)layer
{
    self.mySublayer.frame = self.bounds;
}

答案 4 :(得分:1)

我用虚线边框

创建时的解决方案
class DashedBorderView: UIView {

   let dashedBorder = CAShapeLayer()

   override init(frame: CGRect) {
       super.init(frame: frame)
       commonInit()
   }

   required init?(coder aDecoder: NSCoder) {
       super.init(coder: aDecoder)
       commonInit()
   }

   private func commonInit() {
       //custom initialization
       dashedBorder.strokeColor = UIColor.red.cgColor
       dashedBorder.lineDashPattern = [2, 2]
       dashedBorder.frame = self.bounds
       dashedBorder.fillColor = nil
       dashedBorder.cornerRadius = 2
       dashedBorder.path = UIBezierPath(rect: self.bounds).cgPath
       self.layer.addSublayer(dashedBorder)
   }

   override func layoutSublayers(of layer: CALayer) {
       super.layoutSublayers(of: layer)
       dashedBorder.path = UIBezierPath(rect: self.bounds).cgPath
       dashedBorder.frame = self.bounds
   }
}

答案 5 :(得分:0)

Swift 3.x KVO解决方案 (更新了@ arango_86的答案)

添加观察者

self.addObserver(
    self, 
    forKeyPath: "<YOUR_VIEW>.bounds", 
    options: .new, 
    context: nil
)

观察值

override open func observeValue(
    forKeyPath keyPath: String?, 
    of object: Any?, 
    change: [NSKeyValueChangeKey : Any]?, 
    context: UnsafeMutableRawPointer?
) {
    if (keyPath == "<YOUR_VIEW.bounds") {
      updateDashedShapeLayerFrame()
      return
    }

    super.observeValue(
        forKeyPath: keyPath, 
        of: object, 
        change: change, 
        context: context
    )
}

答案 6 :(得分:0)

当我必须应用KVO时,我更喜欢用RxSwift来做(仅当我将它用于更多东西时,才不要为此添加该库。)

您也可以在该库中或在viewDidLayoutSubviews中应用KVO,但是这样做的效果更好。

  view.rx.observe(CGRect.self, #keyPath(UIView.bounds))
        .subscribe(onNext: { [weak self] in
            guard let bounds = $0 else { return }
            self?.YourLayer.frame = bounds
        })
        .disposed(by: disposeBag)

答案 7 :(得分:0)

开始使用arango_86's answer –如果要在自己的UIView子类中应用KVO修复程序,更“快速”的方法是重写bounds并在其上使用didSet ,就像这样:

override var bounds: CGRect {
    didSet {
        layer.frame = bounds
    }
}

答案 8 :(得分:-2)

我遇到了类似的问题 - 需要设置&#39; CALayer&#39;使用带视图的自动布局时(在代码中,而不是 IB )。

就我而言,我有一个稍微复杂的层次结构,在视图控制器中有一个视图控制器。我最后讨论了这个问题并研究了使用viewDidLayoutSubviews的方法。那不行。万一你的情况与我自己的情况相似,这就是我发现的......

<强>概述

我想使用自动布局约束为CAGradientLayer中的UIView UIViewController gradientView设置框架。

调用子视图child_viewController和视图控制器child_viewController

view是一个视图控制器,我将其设置为一种可重复使用的组件。因此,child_viewController的{​​{1}}被组合成父视图控制器 - 调用parent_viewController

调用viewDidLayoutSubviews的{​​{1}}后,child_viewController的{​​{1}}尚未设置。

(此时,我建议在周围散布一些frame语句,以了解层次结构中视图创建的顺序等。)

所以我继续尝试使用gradientView。但是,由于NSLog的嵌套特性,我发现viewDidAppear未被调用。

(见这个问题:viewWillAppear, viewDidAppear not being called, not firing)。

我目前的解决方案

我已在child_viewController添加了viewDidAppear,并在那里viewDidAppear呼叫parent_viewController

对于初始加载,我需要viewDidAppear,直到在child_viewController中调用所有子视图都设置了它们的框架为止。然后我可以为viewDidAppear ...

设置框架

我已经说过这是我的当前解决方案,因为我对此并不特别满意。

在初始设置child_viewController的框架后 - 如果布局发生变化,该框架可能会无效 - 例如旋转设备。

为了解决这个问题,我使用CAGradientLayer中的CAGradientLayer来保持viewDidLayoutSubviews的框架在child_viewController内,正确无误。

它有效,但感觉不舒服。 (有更好的方法吗?)