CALayers không bị thay đổi kích thước khi thay đổi giới hạn của UIView. Tại sao?


100

Tôi có một UIViewtrong đó có khoảng 8 CALayerlớp con khác nhau được thêm vào lớp của nó. Nếu tôi sửa đổi giới hạn của chế độ xem (hoạt ảnh), thì chế độ xem sẽ tự thu nhỏ lại (tôi đã kiểm tra nó bằng a backgroundColor), nhưng kích thước của lớp con vẫn không thay đổi .

Làm thế nào để giải quyết điều này?

Câu trả lời:


149

Tôi đã sử dụng cùng một phương pháp mà Solin đã sử dụng, nhưng có một lỗi đánh máy trong mã đó. Phương pháp nên là:

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

Đối với mục đích của tôi, tôi luôn muốn lớp con có kích thước đầy đủ của chế độ xem gốc. Đặt phương thức đó trong lớp xem của bạn.


8
@fishinear bạn có chắc không? nó giống như bạn đang mô tả khung. giới hạn, về mặt lý thuyết, luôn luôn có 0,0 là gốc của nó.
Griotspeak

3
@griotspeak - thực tế không phải vậy. Xem mô tả của thuộc tính giới hạn tại đây: developer.apple.com/library/ios/#documentation/uikit/reference/… - "Theo mặc định, điểm gốc của hình chữ nhật giới hạn được đặt thành (0, 0) nhưng bạn có thể thay đổi giá trị này để hiển thị các phần khác nhau của chế độ xem. "
jdc

Rất cám ơn, tôi đã biết một số trường hợp (xem cuộn, cho một) trong đó điều đó không đúng nhưng tôi đã quên về nhận xét này.
griotspeak vào

31
Đừng quên gọi [super layoutSubviews], vì điều này có thể gây ra hành vi không mong muốn trong các lớp UIKit khác nhau.
Kpmurphy91

2
Điều này không hoạt động tốt với tôi với UITextViewbố cục tự định cỡ (scrollEnabled = NO) và tự động. Tôi đã có một chế độ xem văn bản được ghim vào chế độ xem siêu cao của nó, chế độ này có một CALayerở dưới cùng của chính nó (một đường viền) và tôi muốn nó cập nhật vị trí đường viền khi chiều cao của chế độ xem văn bản thay đổi. Vì một số lý do layoutSubiewsđược gọi là quá muộn (và vị trí lớp đã được làm động).
iosdude

26

Vì CALayer trên iPhone không hỗ trợ trình quản lý bố cục, tôi nghĩ bạn phải đặt lớp chính của chế độ xem của mình thành lớp con CALayer tùy chỉnh trong đó bạn ghi đè layoutSublayersđể đặt khung của tất cả các lớp con. Bạn cũng phải ghi đè +layerClassphương thức của chế độ xem để trả về lớp của lớp con CALayer mới của bạn.


Override + layerClass giống như trong trường hợp ghi đè lớp OpenGL? Nghĩ vậy. Đặt khung của các lớp con? Đặt thành gì? Tôi phải tính toán tất cả các khung / vị trí của lớp con riêng lẻ tùy thuộc vào kích thước khung của lớp chồng? Không có bất kỳ giải pháp tương tự như "ràng buộc" hoặc "liên kết đến lớp siêu lớp" nào? Ôi trời, một ngày khác ngoài khung giờ. CGLayers đã được thay đổi kích thước, nhưng quá chậm, CALayers đủ nhanh, nhưng không được thay đổi kích thước. Thật nhiều bất ngờ. Cảm ơn vì đã trả lời, dù sao.
Geri Borbás

3
CALayer trên Mac có trình quản lý bố cục cho việc này nhưng chúng không khả dụng trên iPhone. Vì vậy, có, bạn phải tự tính toán kích thước khung hình. Một tùy chọn khác có thể là sử dụng các lượt xem con thay vì các lớp con. Sau đó, bạn có thể đặt mặt nạ tự động phục hồi của lượt xem phụ cho phù hợp.
Ole Begemann

2
Ya, UIViews là các lớp hiệu suất quá đắt, đó là lý do tại sao tôi sử dụng CALayers.
Geri Borbás

Tôi đã thử theo cách bạn gợi ý. Nó hoạt động, và nó không. Tôi thay đổi kích thước chế độ xem hoạt ảnh, nhưng các lớp con thay đổi kích thước trong một hoạt ảnh khác. Chết tiệt, làm sao bây giờ? Phương thức nào để ghi đè trong lớp con CALayer những gì gọi ra trên MỌI khung (!) Của hoạt ảnh của chế độ xem? Có cái nào không?
Geri Borbás

Chỉ tình cờ gặp cái này. Có vẻ như lựa chọn tốt nhất là phân lớp CALayer như Ole đã nói, nhưng nó có vẻ rườm rà không cần thiết.
Dimitri

14

Tôi đã sử dụng cái này trong UIView.

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

super.layoutSublayersOfLayer(layer)
}

Làm việc cho tôi.


Có, điều này đã hoạt động với tôi trong iOS 8. Có lẽ sẽ hoạt động trong các phiên bản trước đó. Tôi đã có một lớp nền gradient và cần thay đổi kích thước lớp khi thiết bị được xoay.
EPage_Ed

Làm việc cho tôi, nhưng nó đã cần một cuộc gọi thông qua siêu
entropy

Đây là câu trả lời tốt nhất! Vâng, tôi đã thử layoutSubviews nhưng nó chỉ phá vỡ bố cục của UITextField của tôi như leftView và rightView biến mất và nhiều Văn bản trong UITextField biến mất. Có lẽ tôi đã sử dụng tiện ích mở rộng thay vì kế thừa của UIView, nhưng nó rất lỗi. CÔNG TRÌNH NÀY TUYỆT VỜI! THX
Michał Ziobro

Đối với một lớp có ( CALayerDelegatecác draw(_:in:)) bản vẽ tùy chỉnh , tôi cũng phải gọi _anySubLayer.setNeedsDisplay(). Sau đó, điều này làm việc cho tôi.
jedwidz

9

Tôi đã từng gặp vấn đề tương tự. Trong lớp của chế độ xem tùy chỉnh, tôi đã thêm hai lớp con nữa. Để thay đổi kích thước các lớp con (mỗi khi ranh giới của chế độ xem tùy chỉnh thay đổi), tôi đã triển khai phương pháp layoutSubviewscủa chế độ xem tùy chỉnh của mình; bên trong phương pháp này, tôi chỉ cập nhật từng khung của lớp con để phù hợp với ranh giới hiện tại của lớp của chế độ xem con của tôi.

Một cái gì đó như thế này:

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

16
FYI, bạn không cần if (sublayer1 = nil!), Vì ObjC định nghĩa thông điệp tới bằng không (trong trường hợp này, -setFrame :) như một không-op trở về 0.
Andrew Pouliot

7

2017

Câu trả lời theo nghĩa đen cho câu hỏi này:

"CALayers không bị thay đổi kích thước khi thay đổi giới hạn UIView của nó. Tại sao?"

đó là tốt hơn hay tệ hơn

needsDisplayOnBoundsChange

mặc định là false trong CALayer.

giải pháp,

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

Thật vậy, tôi hướng bạn đến QA này

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

trong đó giải thích, contentsScalecài đặt quan trọng làm cái quái gì ; bạn thường cần thiết lập như nhau khi bạn đặt needDisplayOnBoundsChange.


5

Phiên bản Swift 3

Trong ô tùy chỉnh, thêm dòng sau

Khai báo trước

let gradientLayer: CAGradientLayer = CAGradientLayer()

Sau đó thêm các dòng sau

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

0

Như [Ole] đã viết CALayer không hỗ trợ tự động lưu trữ trên iOS. Vì vậy, bạn nên điều chỉnh bố cục theo cách thủ công. Tùy chọn của tôi là điều chỉnh khung của lớp trong (iOS 7 trở về trước)

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

hoặc (kể từ iOS 8)

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

0

Trong chế độ xem tùy chỉnh của bạn, bạn cần khai báo biến cho lớp tùy chỉnh của mình, không khai báo biến trong init phạm vi. Và chỉ cần init nó một lần, đừng thử đặt giá trị null và bắt đầu lại

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

        ghi đè layout funcSubviews () {
            super.layoutSubviews ()
    // bảo vệ let _fillColor = self._fillColor else {return}
            initializeLayout ()
        }
        private func initializeLayout () { 
            customLayer.removeFromSuperView ()
            customLayer.frame = layer.bounds
                   
            layer.insertSubview (tại: 0)
        }
    }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.