CALayers는 UIView의 경계 변경에 따라 크기가 조정되지 않았습니다. 왜?


101

나는이 UIView팔에 대해 다른이있는 CALayer그 레이어에 추가 하위 레이어. 경우 내가보기의 경계 수정 (애니메이션)을, 다음 뷰 자체는 (내가 그것을 확인 축소 backgroundColor), 그러나 서브 레이어 '의 크기는 변경되지 않습니다 .

이것을 해결하는 방법?

답변:


149

Solin이 사용한 것과 동일한 접근 방식을 사용했지만 해당 코드에 오타가 있습니다. 방법은 다음과 같아야합니다.

- (void)layoutSubviews {
  [super layoutSubviews];
  // resize your layers based on the view's new bounds
  mylayer.frame = self.bounds;
}

내 목적을 위해 항상 하위 레이어가 상위 뷰의 전체 크기가되기를 원했습니다. 해당 메소드를 뷰 클래스에 넣으십시오.


8
@fishinear 확실합니까? 프레임을 설명하는 것 같습니다. 이론적으로 경계는 항상 원점으로 0,0을 가져야합니다.
griotspeak

3
@griotspeak-실제로는 그렇지 않습니다. 다음에서 bounds 속성에 대한 설명을 참조하세요. developer.apple.com/library/ios/#documentation/uikit/reference/…- "기본적으로 경계 사각형의 원점은 (0, 0)으로 설정되어 있지만 보기의 다른 부분을 표시하려면이 값을 변경하십시오. "
jdc 2013 년

많은 감사합니다. 사실이 아닌 몇 가지 사례 (스크롤보기)를 배웠지 만이 설명을 잊어 버렸습니다.
griotspeak의 2013 년

31
[super layoutSubviews]를 호출하는 것을 잊지 마십시오. 다양한 UIKit 클래스에서 예기치 않은 동작이 발생할 수 있습니다.
Kpmurphy91 2014 년

2
이것은 자동 크기 조정 UITextView(scrollEnabled = NO) 및 자동 레이아웃으로 저에게 잘 맞지 않았습니다 . 수퍼 뷰에 고정 된 텍스트 뷰가 있었는데,이 뷰 CALayer는 맨 아래에 (테두리)가 있었고, 텍스트 뷰의 높이가 변경되면 테두리 위치를 업데이트하고 싶었습니다. 어떤 이유로 layoutSubiews너무 늦게 호출되었습니다 (그리고 레이어 위치가 애니메이션되었습니다).
iosdude

26

iPhone의 CALayer는 레이아웃 관리자를 지원하지 않기 때문에 뷰의 메인 레이어 layoutSublayers를 모든 하위 레이어의 프레임을 설정하기 위해 재정의하는 사용자 정의 CALayer 하위 클래스로 만들어야한다고 생각합니다 . 또한 +layerClass새 CALayer 하위 클래스의 클래스를 반환하려면 뷰의 메서드를 재정의해야합니다 .


OpenGL 레이어 재정의의 경우처럼 + layerClass를 재정의 하시겠습니까? 그렇게 생각하십시오. 하위 레이어의 프레임을 설정 하시겠습니까? 무엇으로 설정 하시겠습니까? 수퍼 레이어 프레임 크기에 따라 모든 서브 레이어의 프레임 / 위치를 개별적으로 계산해야합니까? "제약과 유사한"또는 "슈퍼 레이어에 링크"와 같은 솔루션이 없습니까? 오, 세상에, 다른 날이 틀렸어. CGLayers의 크기가 조정되었지만 너무 느려 CALayers는 충분히 빠르지 만 크기가 조정되지 않았습니다. 너무 많은 놀라움. 어쨌든 답장을 보내 주셔서 감사합니다.
Geri Borbás

3
Mac의 CALayer에는이를위한 레이아웃 관리자가 있지만 iPhone에서는 사용할 수 없습니다. 그렇습니다. 프레임 크기를 직접 계산해야합니다. 또 다른 옵션은 하위 레이어 대신 하위 뷰를 사용하는 것입니다. 그런 다음 그에 따라 하위 뷰의 자동 크기 조정 마스크를 설정할 수 있습니다.
Ole Begemann

2
Ya, UIViews는 성능이 너무 비싼 클래스이므로 CALayers를 사용합니다.
Geri Borbás

나는 당신이 제안한 방식을 시도했습니다. 작동하지만 그렇지 않습니다. 애니메이션 된 뷰의 크기를 조정하지만 다른 애니메이션에서 하위 레이어의 크기가 조정됩니다. 젠장, 이제 어떡해? 뷰 애니메이션의 모든 (!) 프레임에서 호출되는 CALayer 하위 클래스에서 재정의하는 방법은 무엇입니까? 있어요?
Geri Borbás

이것도 만났습니다. Ole이 말한 것처럼 CALayer를 하위 클래스로 만드는 것이 가장 좋은 옵션 인 것 같지만 불필요하게 번거로운 것 같습니다.
Dimitri

15

나는 이것을 UIView에서 사용했습니다.

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

super.layoutSublayersOfLayer(layer)
}

나를 위해 작동합니다.


예, 이것은 iOS 8에서 저에게 효과적이었습니다. 아마도 이전 버전에서 작동했을 것입니다. 그라데이션 배경 레이어가 있었고 장치를 회전 할 때 레이어 크기를 조정해야했습니다.
EPage_Ed

나를 위해 일했다,하지만 슈퍼에 이르기까지 호출이 필요 않았다
엔트로피

이것이 최고의 답변입니다! 네, layoutSubviews를 시도했지만 leftView 및 rightView와 같은 UITextField의 레이아웃이 사라지고 UITextField의 더 많은 텍스트가 사라집니다. 아마도 UIView 상속 대신 확장을 사용했지만 매우 버그가 많았습니다. 이것은 훌륭하게 작동합니다! THX
Michał Ziobro

사용자 정의 도면 ( CALayerDelegate's draw(_:in:))이 있는 레이어의 경우 _anySubLayer.setNeedsDisplay(). 그런 다음 이것은 나를 위해 일했습니다.
jedwidz

9

나는 같은 문제가 있었다. 사용자 정의보기의 레이어에서 두 개의 하위 레이어를 더 추가했습니다. 하위 레이어의 크기를 조정하기 위해 (사용자 정의보기의 경계가 변경 될 때마다) 사용자 정의보기의 방법 layoutSubviews을 구현했습니다 . 이 방법 내에서 내 하위 뷰 레이어의 현재 경계와 일치하도록 각 하위 레이어의 프레임을 업데이트합니다.

이 같은:

-(void)layoutSubviews{
   //keep the same origin, just update the width and height
   if(sublayer1!=nil){
      sublayer1.frame = self.layer.bounds;
   }
}

16
참고로, ObjC는 0을 반환하는 no-op으로 메시지를 nil (이 경우 -setFrame :)로 정의하므로 if (sublayer1! = nil)이 필요하지 않습니다.
Andrew Pouliot

7

2017 년

이 질문에 대한 문자적인 대답 :

"CALayers는 UIView의 경계 변경에 따라 크기가 조정되지 않았습니다. 이유가 무엇입니까?"

그게 좋든 나쁘 든

needsDisplayOnBoundsChange

에서 기본값은 false입니다 CALayer.

해결책,

class CircularGradientViewLayer: CALayer {
    
    override init() {
        
        super.init()
        needsDisplayOnBoundsChange = true
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override open func draw(in ctx: CGContext) {
        go crazy drawing in .bounds
    }
}

이 QA로 안내합니다.

https://stackoverflow.com/a/47760444/294884

도대체 중요한 contentsScale설정이 하는 일을 설명합니다 . 일반적으로 needsDisplayOnBoundsChange를 설정할 때 동일하게 설정해야합니다.


5

Swift 3 버전

사용자 지정 셀에서 다음 줄 추가

먼저 선언

let gradientLayer: CAGradientLayer = CAGradientLayer()

그런 다음 다음 줄을 추가하십시오.

override func layoutSubviews() {
    gradientLayer.frame = self.YourCustomView.bounds
}

0

[Ole]이 작성한 CALayer는 iOS에서 자동 크기 조정을 지원하지 않습니다. 따라서 레이아웃을 수동으로 조정해야합니다. 내 옵션은 (iOS 7 및 이전 버전) 내에서 레이어의 프레임을 조정하는 것이 었습니다.

- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration 

또는 (iOS 8 기준)

- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id <UIViewControllerTransitionCoordinator>)coordinato

0

사용자 정의보기에서 사용자 정의 레이어에 대한 변수를 선언해야합니다. 범위 초기화에서 변수를 선언하지 마십시오. 그리고 한 번만 초기화하고 null 값을 설정하고 다시 초기화하지 마십시오.

    class CustomView : UIView {
        var customLayer : CALayer = CALayer ()

        func layoutSubviews () {
            super.layoutSubviews ()
    // 가드 let _fillColor = self._fillColor else {return}
            initializeLayout ()
        }
        private func initializeLayout () { 
            customLayer.removeFromSuperView ()
            customLayer.frame = layer.bounds
                   
            layer.insertSubview (at : 0)
        }
    }
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.