CALayers ไม่ได้รับการปรับขนาดตามการเปลี่ยนแปลงขอบเขตของ UIView ทำไม?


101

ฉันมีชั้นย่อยที่เพิ่มเข้ามาในเลเยอร์UIViewประมาณ 8 CALayerชั้น ถ้าผมปรับเปลี่ยนมุมมองขอบเขตของ (animated) แล้วมุมมองของตัวเองหดตัว (ฉันจะตรวจสอบด้วยbackgroundColor) แต่ขนาด sublayers' ยังคงไม่เปลี่ยนแปลง

วิธีแก้ปัญหานี้?

คำตอบ:


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 - นั่นไม่ใช่อย่างนั้นจริงๆ ดูคำอธิบายของคุณสมบัติขอบเขตที่นี่: developer.apple.com/library/ios/#documentation/uikit/reference/… - "โดยค่าเริ่มต้นจุดเริ่มต้นของสี่เหลี่ยมผืนผ้าขอบเขตจะตั้งค่าเป็น (0, 0) แต่คุณสามารถ เปลี่ยนค่านี้เพื่อแสดงส่วนต่างๆของมุมมอง "
— jdc

ขอบคุณมากฉันได้เรียนรู้บางกรณี (มุมมองแบบเลื่อนสำหรับกรณีหนึ่ง) ซึ่งไม่เป็นความจริง แต่ฉันลืมเกี่ยวกับความคิดเห็นนี้
— griotspeak

31
อย่าลืมเรียกใช้ [super layoutSubviews] เพราะอาจทำให้เกิดพฤติกรรมที่ไม่คาดคิดในคลาส UIKit ต่างๆ
— Kpmurphy91

2
สิ่งนี้ไม่ได้ผลดีสำหรับฉันด้วยการปรับขนาดตัวเองUITextView(scrollEnabled = NO) และรูปแบบอัตโนมัติ ฉันมีมุมมองข้อความที่ตรึงไว้ที่ superview ซึ่งจะมีCALayerที่ด้านล่างของตัวมันเอง (เส้นขอบ) และฉันต้องการให้มันอัปเดตตำแหน่งเส้นขอบเมื่อความสูงของมุมมองข้อความเปลี่ยนไป ด้วยเหตุผลบางอย่างlayoutSubiewsเรียกว่าสายเกินไป (และตำแหน่งเลเยอร์เป็นภาพเคลื่อนไหว)
— iosdude

26

เนื่องจาก CALayer บน iPhone ไม่รองรับตัวจัดการเลย์เอาต์ฉันคิดว่าคุณต้องทำให้เลเยอร์หลักของมุมมองของคุณเป็นคลาสย่อยของ CALayer ที่กำหนดเองซึ่งคุณจะแทนที่layoutSublayersเพื่อตั้งค่าเฟรมของเลเยอร์ย่อยทั้งหมด คุณต้องลบล้าง+layerClassเมธอดของมุมมองของคุณด้วยเพื่อส่งคืนคลาสของคลาสย่อย CALayer ใหม่ของคุณ


Override + layerClass เหมือนในกรณีของ OpenGL layer override? คิดอย่างนั้น. ตั้งค่าเฟรมของชั้นย่อย? ตั้งค่าเป็นอะไร ฉันต้องคำนวณเฟรม / ตำแหน่งของเลเยอร์ย่อยทั้งหมดทีละรายการขึ้นอยู่กับขนาดเฟรมซูเปอร์เลเยอร์? ไม่มีโซลูชันแบบ "เหมือนข้อ จำกัด " หรือ "ลิงก์ไปยังซูเปอร์เลเยอร์" หรือไม่? โอ้พระเจ้าอีกวันนอกกรอบเวลา CGLayers ได้รับการปรับขนาดแล้ว แต่ล้าเกินไป CALayers เร็วพอ แต่ไม่ได้รับการปรับขนาด แปลกใจมากมาย ขอบคุณสำหรับการตอบกลับอย่างไรก็ตาม
— Geri Borbás

3
CALayer บน Mac มีตัวจัดการเลย์เอาต์สำหรับสิ่งนี้ แต่ไม่มีใน iPhone ใช่คุณต้องคำนวณขนาดเฟรมด้วยตัวเอง อีกทางเลือกหนึ่งคือใช้มุมมองย่อยแทนชั้นย่อย จากนั้นคุณสามารถตั้งค่ามาสก์การปรับขนาดอัตโนมัติของมุมมองย่อยตามนั้น
— Ole Begemann

2
ใช่ UIViews เป็นคลาสที่มีประสิทธิภาพสูงเกินไปฉันจึงใช้ CALayers
— Geri Borbás

ฉันลองทำตามที่คุณแนะนำแล้ว มันใช้งานได้และไม่เป็นเช่นนั้น ฉันปรับขนาดมุมมองเป็นภาพเคลื่อนไหว แต่ชั้นย่อยจะปรับขนาดในภาพเคลื่อนไหวอื่น Bloody hell จะทำยังไงดี? อะไรคือวิธีการแทนที่ในคลาสย่อยของ CALayer สิ่งที่เรียกใช้ในทุกเฟรม (!) ของภาพเคลื่อนไหวของมุมมอง? มีผู้ใด?
— Geri Borbás

ชนแค่นี้เหมือนกัน ดูเหมือนว่าตัวเลือกที่ดีที่สุดคือการเลือกคลาส CALayer ตามที่ Ole กล่าวไว้ แต่ดูเหมือนจะยุ่งยากโดยไม่จำเป็น
— Dimitri

15

ฉันใช้สิ่งนี้ใน UIView

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

super.layoutSublayersOfLayer(layer)
}

ใช้ได้ผลสำหรับฉัน


ใช่สิ่งนี้ใช้ได้กับฉันใน iOS 8 ซึ่งน่าจะใช้ได้ในเวอร์ชันก่อนหน้านี้ ฉันมีเลเยอร์พื้นหลังไล่ระดับสีและจำเป็นต้องปรับขนาดเลเยอร์เมื่อหมุนอุปกรณ์
— EPage_Ed

ทำงานให้ฉัน แต่มันไม่จำเป็นต้องโทรผ่านไปยังซูเปอร์
— เอนโทรปี

นี่คือคำตอบที่ดีที่สุด! ใช่ฉันได้ลองใช้ LayoutSubviews แล้ว แต่มันแบ่งเพียงเลย์เอาต์ของ UITextField ของฉันเช่น leftView และ rightView ที่หายไปและข้อความอื่น ๆ ใน 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
FYI คุณไม่จำเป็นต้องใช้ if (sublayer1! = nil) เนื่องจาก ObjC กำหนดข้อความเป็นศูนย์ (ในกรณีนี้คือ -setFrame :) เป็น no-op ที่ส่งคืน 0
— Andrew Pouliot

7

พ.ศ. 2560

คำตอบที่แท้จริงสำหรับคำถามนี้:

"CALayers ไม่ได้รับการปรับขนาดตามการเปลี่ยนแปลงขอบเขตของ UIView เพราะเหตุใด"

จะดีขึ้นหรือแย่ลง

needsDisplayOnBoundsChange

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

ในมุมมองที่กำหนดเองคุณต้องประกาศตัวแปรสำหรับเลเยอร์ที่กำหนดเองของคุณอย่าประกาศตัวแปรในขอบเขตการเริ่มต้น และเริ่มต้นเพียงครั้งเดียวอย่าพยายามตั้งค่าว่างและเริ่มต้นใหม่

    คลาส CustomView: UIView {
        var customLayer: CALayer = CALayer ()

        แทนที่เค้าโครง funcSubviews () {
            super.layoutSubviews ()
    // guard let _fillColor = self._fillColor else {return}
            initializeLayout ()
        }
        func ส่วนตัว initializeLayout () { 
            customLayer.removeFromSuperView ()
            customLayer.frame = layer.bounds
                   
            layer.insertSubview (ที่: 0)
        }
    }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.