Có sự kiện thay đổi kích thước UIView không?


102

Tôi có một chế độ xem có các hàng và cột của các chế độ xem hình ảnh trong đó.

Nếu chế độ xem này được thay đổi kích thước, tôi cần sắp xếp lại các vị trí của chế độ xem ảnh.

Chế độ xem này là một chế độ xem phụ của một chế độ xem khác được thay đổi kích thước.

Có cách nào để phát hiện khi nào chế độ xem này đang được thay đổi kích thước không?


Khi nào chế độ xem được thay đổi kích thước? Khi thiết bị xoay hoặc khi người dùng xoay thiết bị bằng cách sử dụng cảm ứng đa điểm?
Evan Mulawski

Chế độ xem này được thay đổi kích thước khi người dùng chạm vào một nút trên chế độ xem khác (chế độ xem chính).
live-love

Câu trả lời:


98

Như Uli nhận xét bên dưới, cách thích hợp để làm điều đó là ghi đè layoutSubviewsvà bố trí các imageView ở đó.

Nếu vì lý do nào đó, bạn không thể phân lớp và ghi đè layoutSubviews, thì việc quan sát boundssẽ hoạt động, ngay cả khi hơi bẩn. Tệ hơn nữa, có rủi ro khi quan sát - Apple không đảm bảo KVO hoạt động trên các lớp UIKit. Đọc cuộc thảo luận với kỹ sư của Apple tại đây: Khi nào một đối tượng liên quan được phát hành?

câu trả lời ban đầu:

Bạn có thể sử dụng quan sát khóa-giá trị:

[yourView addObserver:self forKeyPath:@"bounds" options:0 context:nil];

và thực hiện:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    if (object == yourView && [keyPath isEqualToString:@"bounds"]) {
        // do your stuff, or better schedule to run later using performSelector:withObject:afterDuration:
    }
}

2
Thực sự, việc bố trí các subview chính xác là mục đích của layoutSubviews, vì vậy việc quan sát các thay đổi về kích thước, trong khi chức năng, là thiết kế xấu của IMO.
uliwitness

@uliwitness tốt họ tiếp tục thay đổi những thứ này. Dù sao, bây giờ bạn nên sử dụng viewWillTransition, v.v.
Dan Rosenstark

viewWillTransition dành cho UIViewControllers, không phải UIView.
Armand

Việc sử dụng layoutSubviewsvẫn là phương pháp được khuyến nghị nếu, tùy thuộc vào kích thước hiện tại của chế độ xem, các chế độ xem phụ khác nhau cần được thêm / xóa và các ràng buộc khác nhau cần được thêm / bớt?
Chris Prince

1
Vâng, tôi nghĩ tôi chỉ trả lời điều này cho trường hợp của tôi. Khi tôi thực hiện thiết lập (phụ thuộc vào kích thước), tôi cần ghi đè giới hạn, nó sẽ hoạt động. Khi tôi làm điều đó trong layoutSubviews thì không.
Chris Prince

93

Trong một UIViewlớp con, các trình quan sát thuộc tính có thể được sử dụng:

override var bounds: CGRect {
    didSet {
        // ...
    }
}

Không có phân lớp, quan sát khóa-giá trị với thông minh key-đường sẽ làm:

var boundsObservation: NSKeyValueObservation?

func beginObservingBounds() {
    boundsObservation = observe(\.bounds) { capturedSelf, _ in
        // ...
    }
}

2
@Ali Tại sao bạn lại nghĩ như vậy?
Rudolf Adamkovič Ngày

khi khung tải thay đổi nhưng có vẻ như không có giới hạn (tôi biết điều đó thật kỳ lạ và có thể tôi đang làm sai điều gì đó)
Ali

3
@Ali: như nó đã được đề cập một vài lần ở đây: framelà một thuộc tính có nguồn gốc và được tính toán thời gian chạy. không ghi đè điều này, trừ khi bạn có một lý do rất thông minh và biết rõ để làm như vậy. nếu không: sử dụng boundshoặc (thậm chí tốt hơn) layoutSubviews.
auco

3
Một cách tuyệt vời để tạo một vòng tròn. Cảm ơn! override var bounds: CGRect { didSet { layer.cornerRadius = bounds.size.width / 2 }}
SimplGy

4
Tính năng phát hiện boundshoặc framethay đổi không đảm bảo hoạt động, tùy thuộc vào vị trí bạn đặt chế độ xem của mình trong phân cấp chế độ xem. Tôi sẽ ghi đè layoutSubviewsthay thế. Xem điều này và câu trả lời này .
HuaTham

31

Tạo lớp con của UIView và ghi đè layoutSubviews


11
vấn đề với điều này là các lượt xem phụ không chỉ có thể thay đổi kích thước của chúng mà còn có thể tạo hiệu ứng cho sự thay đổi kích thước đó. Khi UIView chạy hoạt ảnh, nó không gọi layoutSubviews mỗi lần.
David Jeske

1
@DavidJeske UIViewAnimationOptions có một tùy chọn được gọi là UIViewAnimationOptionLayoutSubviews. Tôi nghĩ điều này có thể giúp ích. :)
Neal.Marlin

8

Swift 4 keypath KVO - Đây là cách tôi phát hiện tự động xoay và di chuyển sang bảng điều khiển bên iPad. Nên làm việc với bất kỳ góc nhìn nào. Phải quan sát lớp của UIView.

private var observer: NSKeyValueObservation?

override func viewDidLoad() {
    super.viewDidLoad()
    observer = view.layer.observe(\.bounds) { object, _ in
        print(object.bounds)
    }
    // ...
}
override func viewWillDisappear(_ animated: Bool) {
    observer?.invalidate()
    //...
}

Giải pháp này đã làm việc cho tôi. Tôi mới sử dụng Swift và tôi không chắc về cú pháp \ .bounds mà bạn đã sử dụng ở đây. điều đó chính xác có nghĩa là gì?
WBuck


.layerđã lừa! Bạn có biết tại sao sử dụng view.observekhông hoạt động?
d4Rk

@ d4Rk - Tôi không biết. Tôi đoán là lớp đó giới hạn ít mơ hồ hơn các khung UIView. Ví dụ: contentOffset của UITableView sẽ ảnh hưởng đến tọa độ cuối cùng của các subView của nó. Không chắc.
Warren Stringer

ĐÂY LÀ CÂU TRẢ LỜI TUYỆT VỜI CHO TÔI. Trường hợp của tôi là tôi đang sử dụng Tự động thanh toán để đưa ra quan điểm của mình. NHƯNG UICollectionViewFlowLayout buộc bạn phải cung cấp một itemSize rõ ràng. nhưng khi kích thước collectionView của bạn thay đổi (như trong trường hợp của tôi khi tôi hiển thị thanh công cụ với AutoLayout) - itemSize vẫn giữ nguyên và ném = [trình quan sát là cách tiếp cận rõ ràng nhất mà tôi có cho đến nay. và cái hay nhất !!
Yitzchak

7

Bạn có thể tạo một lớp con của UIView và ghi đè lên

khung setFrame: (CGRect)

phương pháp. Đây là phương thức được gọi khi khung (tức là kích thước) của khung nhìn bị thay đổi. Làm điều gì đó như sau:

- (void) setFrame:(CGRect)frame
{
  // Call the parent class to move the view
  [super setFrame:frame];

  // Do your custom code here.
}

Hợp lý và chức năng. Tốt cuộc gọi.
El Zorko

1
FYI Điều này không hoạt động đối với lớp con UIImageView. Thông tin thêm tại đây: stackoverflow.com/questions/19216684/…
William Entriken.

Ngoài ra, setFrame:không được gọi trên UITextViewlớp con của tôi trong quá trình thay đổi kích thước gây ra bởi tự động chuyển đổi trong khi đó layoutSubviews:. Lưu ý: Tôi đang sử dụng bố cục tự động và iOS 7.0.
ma11hew28

3
không bao giờ ghi đè setFrame: . framelà một tài sản có nguồn gốc. Xem câu trả lời của tôi
Adlai Holler

7

Khá cũ nhưng vẫn là một câu hỏi hay. Trong mã mẫu của Apple và trong một số lớp con UIView riêng tư của họ, họ ghi đè setBounds gần giống như:

-(void)setBounds:(CGRect)newBounds {
    BOOL const isResize = !CGSizeEqualToSize(newBounds.size, self.bounds.size);
    if (isResize) [self prepareToResizeTo:newBounds.size]; // probably saves 
    [super setBounds:newBounds];
    if (isResize) [self recoverFromResizing];
}

Ghi đè setFrame: KHÔNG phải là một ý kiến ​​hay. framebắt nguồn từ center, boundstransform, vì vậy iOS sẽ không nhất thiết phải gọi setFrame:.


2
Điều này đúng (trên lý thuyết). Tuy nhiên, setBounds:nó cũng không được gọi khi đặt thuộc tính khung (ít nhất là trên iOS 7.1). Đây có thể là một sự tối ưu hóa mà Apple thêm vào để tránh thông báo bổ sung.
nschum

1
… Và thiết kế tồi của Apple khi không gọi những người truy cập của chính họ.
osxdirk

1
Trên thực tế, cả hai frameboundsđều bắt nguồn từ cơ bản của chế độ xem CALayer; họ chỉ gọi thông qua getter của lớp. Và setFrame:thiết lập khung của lớp, trong khi setBounds:thiết lập giới hạn của lớp. Vì vậy, bạn không thể chỉ ghi đè cái này hay cái kia. Ngoài ra, layoutSubviewsđược gọi quá mức (không chỉ do thay đổi hình học), vì vậy nó có thể không phải lúc nào cũng là một lựa chọn tốt. Vẫn đang tìm kiếm ...
big_m

-3

Nếu bạn đang ở trong một phiên bản UIViewController, viewDidLayoutSubviewsthì việc ghi đè sẽ là một mẹo nhỏ.

override func viewDidLayoutSubviews() {
    // update subviews
}

Cần thay đổi kích thước UIView, không phải UIViewController.
Gastón Antonio Montes

@ GastónAntonioMontes cảm ơn vì điều đó! Bạn có thể nhận thấy mối quan hệ giữa các UIViewphiên bản và UIViewControllerphiên bản. Vì vậy, nếu bạn có một UIViewtrường hợp không có VC đính kèm, các câu trả lời khác là tuyệt vời, nhưng nếu bạn tình cờ gắn với VC, đây là người của bạn. Xin lỗi nó không áp dụng cho trường hợp của bạn.
Dan Rosenstark
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.