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.