CALayersは、UIViewの境界変更でサイズ変更されませんでした。どうして?


100

私はその層に追加されたUIView約8つの異なる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
さまざまなUIKitクラスで予期しない動作が発生する可能性があるため、[super layoutSubviews]を呼び出すことを忘れないでください。
Kpmurphy91

2
これは、セルフサイズUITextView(scrollEnabled = NO)と自動レイアウトでは、うまく機能しませんでした。テキストビューをスーパービューに固定しました。スーパービューCALayerは下部に(ボーダー)があり、テキストビューの高さが変更されたときにボーダーの位置を更新したいと思いました。なんらかの理由layoutSubiewsで呼び出されるのが遅すぎた(そしてレイヤーの位置がアニメーション化された)。
iosdude 2017年

26

iPhoneのCALayerはレイアウトマネージャーをサポートしていないので、ビューのメインレイヤーを、layoutSublayersすべてのサブレイヤーのフレームを設定するためにオーバーライドするカスタムCALayerサブクラスにする必要があると思います。+layerClass新しいCALayerサブクラスのクラスを返すには、ビューのメソッドもオーバーライドする必要があります。


OpenGLレイヤーオーバーライドの場合のように+ layerClassをオーバーライドしますか?そう思う。サブレイヤーのフレームを設定しますか?何に設定しますか?スーパーレイヤーのフレームサイズに応じて、すべてのサブレイヤーのフレーム/位置を個別に計算する必要がありますか?「制約のような」、または「スーパーレイヤーへのリンク」のような解決策はありませんか?ああ、神様、時間枠外の別の日。CGLayersはサイズ変更されましたが、遅すぎました。CALayersは十分高速ですが、サイズ変更されませんでした。多くの驚き。とにかく返信ありがとうございます。
GeriBorbás10年

3
MacのCALayerには、このためのレイアウトマネージャがありますが、iPhoneでは使用できません。したがって、フレームサイズを自分で計算する必要があります。別のオプションは、サブレイヤーの代わりにサブビューを使用することです。次に、それに応じてサブビューの自動サイズ変更マスクを設定できます。
Ole Begemann 2010年

2
うん、UIViewsはパフォーマンスが高すぎるクラスなので、CALayersを使用します。
GeriBorbás10年

私はあなたが提案した方法を試しました。動作しますが、動作しません。アニメーションビューのサイズを変更しましたが、サブレイヤーは別のアニメーションでサイズ変更します。血まみれの地獄、今何をすべきか?ビューのアニメーションのすべての(!)フレームで呼び出されるCALayerサブクラスでオーバーライドするメソッドは何ですか?何かありますか?
GeriBorbás10年

ちょうどこれにもぶつかった。Oleが言ったようにCALayerをサブクラス化することが最良のオプションのようですが、不必要に面倒です。
ディミトリ

14

これをUIViewで使用しました。

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

super.layoutSublayersOfLayer(layer)
}

私のために働く。


はい、これはiOS 8では私にとってはうまくいきました。おそらく以前のバージョンではうまくいくでしょう。グラデーションの背景レイヤーがあり、デバイスが回転したときにレイヤーのサイズを変更する必要がありました。
EPage_Ed 2015年

私にとってはうまくいきましたが、スーパー
エントロピー

これが一番の答えです!うん、私はlayoutSubviewsを試しましたが、leftViewとrightViewが消えてUITextFieldのテキストが消えるようなUITextFieldのレイアウトを壊すだけです。UIViewの継承の代わりに拡張機能を使用した可能性がありますが、非常にバグが多かったです。これは素晴らしい作品です!THX
のMichałZiobro

カスタム描画(と層のためCALayerDelegatedraw(_:in:))、私はまた、呼び出す必要がありました_anySubLayer.setNeedsDisplay()。その後、これは私のために働いた。
jedwidz

9

私も同じ問題を抱えていました。カスタムビューのレイヤーに、さらに2つのサブレイヤーを追加しました。(カスタムビューの境界が変更されるたびに)サブレイヤーのサイズを変更するために、カスタムビューのメソッドを実装しましたlayoutSubviews。このメソッド内では、サブビューのレイヤーの現在の境界に一致するように各サブレイヤーのフレームを更新します。

このようなもの:

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

16
FYI、あなたがもし(sublayer1を= nilを!)必要はありません、にObjC、この場合、-setFrame :) 0を返すノーオペレーションなどで(nilにメッセージを定義しているので
アンドリュー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

カスタムビューでは、カスタムレイヤーの変数を宣言する必要があります。スコープのinitで変数を宣言しないでください。そして一度だけそれを初期化し、null値を設定して再初期化しないでください

    クラスCustomView:UIView {
        var customLayer:CALayer = CALayer()

        func layoutSubviews(){
            super.layoutSubviews()
    //ガードlet _fillColor = self._fillColor else {return}
            initializeLayout()
        }
        プライベートfunc initializeLayout(){ 
            customLayer.removeFromSuperView()
            customLayer.frame = layer.bounds
                   
            layer.insertSubview(at:0)
        }
    }
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.