CALayer IOS 上的自动布局约束

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/24303279/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 00:58:08  来源:igfitidea点击:

Auto Layout constraint on CALayer IOS

iosautolayoutcalayer

提问by nilkash

Hi I am developing iPhone application in which I tried to set one side border for edittext. I did this in following way:

嗨,我正在开发 iPhone 应用程序,我试图在其中设置编辑文本的一侧边框。我通过以下方式做到了这一点:

 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];

I put some constraints on my edittext in IB so that when I rotate my device it will adjust width of text field according to that. My problem is that adjusts the width of edittext not adjusting the width of CALayer which I set for my edit text. So I think I have to put some constraints for my CALayer item as well. But I dont know how to do that. ANy one knows about this? Need Help. Thank you.

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

回答by Daij-Djan

the whole autoresizing business is view-specific. layers don't autoresize.

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

what you have to do -- in code -- is to resize the layer yourself

你必须做的——在代码中——是自己调整图层的大小

e.g.

例如

in a viewController you would do

在 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;
}

or in a custom UIView you could use

或在您可以使用的自定义 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;
}

回答by arango_86

Checkout this solution. Use KVO, for the path "YourView.bounds" as given below.

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

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

Then handle it as given below.

然后按照下面给出的处理它。

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)
    }

For more info, refer the following link.

有关更多信息,请参阅以下链接。

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

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

回答by pckill

According to thisanswer, layoutSubviewsdoes not get called in all needed cases. I have found this delegate method more effective:

根据这个答案,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;
}

回答by Wide Vision

I implemented the method layoutSubviews of my custom view; inside this method I just update each sublayer's frame to match the current boundaries of my subview's layer.

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

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

回答by Marosdee Uma

My solution when I create with dashed border

我用虚线边框创建时的解决方案

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
   }
}

回答by footyapps27

Swift 3.x KVO Solution(Updated @arango_86's answer)

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

Add Observer

添加观察者

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

Observe Values

观察值

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
    )
}

回答by Pablo Sanchez Gomez

When I have to apply KVO I prefer to do it with RxSwift (Only if I am using it for more stuff, do not add this library just for this.)

当我必须应用 KVO 时,我更喜欢使用 RxSwift(只有当我将它用于更多东西时,不要为此添加这个库。)

You can apply KVO with this library too, or in the viewDidLayoutSubviewsbut I've had better results with this.

你也可以在这个库中应用 KVO,或者在这个库中应用 KVO,viewDidLayoutSubviews但我已经有了更好的结果。

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

回答by Gavin Hope

I had a similar problem - needing to set the frame of a 'CALayer' when using auto layout with views (in code, not IB).

我有一个类似的问题 - 在使用带有视图的自动布局时需要设置“CALayer”的框架(在代码中,而不是IB)。

In my case, I had a slightly convoluted hierarchy, having a view controller within a view controller. I ended up at this SO question and looked into the approach of using viewDidLayoutSubviews. That didn't work. Just in case your situation is similar to my own, here's what I found...

就我而言,我有一个稍微复杂的层次结构,在视图控制器中有一个视图控制器。我最终解决了这个问题,并研究了使用viewDidLayoutSubviews. 那没有用。以防万一你的情况和我的类似,这是我发现的......

Overview

概述

I wanted to set the frame for the CAGradientLayerof a UIViewthat I was positioning as a subview within a UIViewControllerusing auto layout constraints.

我想为我定位为使用自动布局约束中的子视图CAGradientLayer的 a设置框架。UIViewUIViewController

Call the subview gradientViewand the view controller child_viewController.

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

child_viewControllerwas a view controller I'd set up as a kind of re-usable component. So, the viewof child_viewControllerwas being composed into a parent view controller - call that parent_viewController.

child_viewController是我设置为一种可重用组件的视图控制器。所以,viewofchild_viewController被组合成一个父视图控制器 - 调用那个parent_viewController.

When viewDidLayoutSubviewsof child_viewControllerwas called, the frameof gradientViewwas not yet set.

viewDidLayoutSubviewsofchild_viewController被调用时,frameofgradientView尚未设置。

(At this point, I'd recommend sprinkling some NSLogstatements around to get a feel for the sequence of creation of views in your hierarchy, etc.)

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

So I moved on to try using viewDidAppear. However, due to the nested nature of child_viewControllerI found viewDidAppearwas not being called.

所以我继续尝试使用viewDidAppear. 但是,由于child_viewController我发现 的嵌套性质viewDidAppear没有被调用。

(See this SO question: viewWillAppear, viewDidAppear not being called, not firing).

(见这个问题:viewWillAppear, viewDidAppear not be called, not fire)。

My current solution

我目前的解决方案

I've added viewDidAppearin parent_viewControllerand from there I'm calling viewDidAppearin child_viewController.

我已经添加viewDidAppearparent_viewController,并从那里我打电话viewDidAppearchild_viewController

For the initial load I need viewDidAppearas it's not until this is called in child_viewControllerthat all of the subviews have their frames set. I can then set the frame for the CAGradientLayer...

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

I've said that this is my current solutionbecause I'm not particularly happy with it.

我说过这是我目前的解决方案,因为我对它不是特别满意。

After initially setting the frame of the CAGradientLayer- that frame can become invalid if the layout changes - e.g. rotating the device.

在初始设置框架后CAGradientLayer- 如果布局更改,该框架可能会变得无效 - 例如旋转设备。

To handle this I'm using viewDidLayoutSubviewsin child_viewController- to keep the frame of the CAGradientLayerwithin gradientView, correct.

为了解决这个问题,我使用viewDidLayoutSubviewschild_viewController-保持的框架CAGradientLayergradientView,正确的。

It works but doesn't feel good. (Is there a better way?)

它有效,但感觉不太好。(有没有更好的办法?)