Khi nào layoutSubview được gọi là?


264

Tôi có một chế độ xem tùy chỉnh không nhận được layoutSubviewtin nhắn trong khi hoạt hình.

Tôi có một cái nhìn lấp đầy màn hình. Nó có một khung nhìn phụ tùy chỉnh ở dưới cùng của màn hình thay đổi kích thước chính xác trong Trình tạo giao diện nếu tôi thay đổi chiều cao của thanh điều hướng. layoutSubviewsđược gọi khi khung nhìn được tạo, nhưng không bao giờ nữa. Bài phỏng vấn của tôi được đặt chính xác. Nếu tôi tắt thanh trạng thái trong cuộc gọi, thì khung nhìn phụ hoàn toàn layoutSubviewskhông được gọi, mặc dù chế độ xem chính sẽ làm thay đổi kích thước của nó.

Trong hoàn cảnh nào layoutSubviewsthực sự được gọi là?

Tôi đã autoresizesSubviewsthiết lập để NOxem tùy chỉnh của tôi. Và trong Interface Builder tôi có các thanh chống trên cùng và dưới cùng và bộ mũi tên dọc.


Một phần khác của câu đố là cửa sổ phải được làm chìa khóa:

[window makeKeyAndVisible];

mặt khác, các cuộc phỏng vấn không được tự động thay đổi kích thước.

Câu trả lời:


492

Tôi đã có một câu hỏi tương tự, nhưng không hài lòng với câu trả lời (hoặc bất kỳ câu hỏi nào tôi có thể tìm thấy trên mạng), vì vậy tôi đã thử nó trong thực tế và đây là những gì tôi nhận được:

  • initkhông gây ra layoutSubviewsđược gọi là (duh)
  • addSubview:nguyên nhân layoutSubviewsđược gọi trên chế độ xem được thêm vào, chế độ xem được thêm vào (chế độ xem mục tiêu) và tất cả các lần xem xét của mục tiêu
  • xem setFrame thông minh các cuộc gọi layoutSubviewstrên chế độ xem có khung được đặt chỉ khi tham số kích thước của khung khác nhau
  • cuộn một UIScrollView gây ra layoutSubviewsđược gọi trên scrollView và giám sát của nó
  • xoay thiết bị chỉ gọi layoutSubviewtrên chế độ xem chính (chế độ xem phản hồi Chế độ xem chính)
  • Thay đổi kích thước một khung nhìn sẽ gọi layoutSubviewsvào giám sát của nó

Kết quả của tôi - http://blog.logichigh.com/2011/03/16/when-does-layoutsubviews-get-called/


1
Câu trả lời chính xác. Tôi đã luôn luôn tự hỏi về layoutSubviews. Có initWithFrame:nguyên nhân layoutSubviewsđược gọi là?
Robert

2
@Robert - Tôi đã sử dụng initWithFrame ... nên không.
BadPirate

8
@BadPirate: . Theo các thí nghiệm của tôi, nếu bạn thay đổi kích thước view1.1nó gọi layoutSubviewscủa view1và sau đó layoutSubviewscủa view1.1. Cuộc gọi này không tuyên truyền vô thời hạn đến superviews, gọi đó là trên view1.1.1thực hiện cuộc gọi layoutSubviewstrên view1.1view1.1.1. Chỉ di chuyển mà không thay đổi kích thước của nó không gọi layoutSubviewsbất kỳ ai trong số họ.
João Portela

1
viewDidLoad không được gọi trên UIView (nhưng UIViewControll). Xem Đã tải được gọi sau khi UIView là init'd.
BadPirate

2
Dựa trên thử nghiệm của tôi, các quy tắc thứ hai có thể không chính xác: khi tôi thêm view1.2vào view1, layoutSubviewscủa view1.2view1được gọi, nhưng layoutSubviewsvề view1.1không được gọi. ( view1.1view1.2là các cuộc phỏng vấn của view1). Đó là, không phải tất cả các cuộc phỏng vấn của chế độ xem đích được gọi là layoutSubviewsphương thức .
HongchaoZhang

96

Dựa trên câu trả lời trước của @BadPirate, tôi đã thử nghiệm thêm một chút và đưa ra một số giải thích / sửa chữa. Tôi thấy rằng layoutSubviews:sẽ được gọi trên một khung nhìn nếu và chỉ khi:

  • Giới hạn của chính nó (không phải khung) thay đổi.
  • Giới hạn của một trong những cuộc phỏng vấn trực tiếp của nó đã thay đổi.
  • Một khung nhìn phụ được thêm vào khung nhìn hoặc xóa khỏi khung nhìn.

Một số chi tiết có liên quan:

  • Các giới hạn được coi là thay đổi chỉ khi giá trị mới khác nhau, bao gồm cả một nguồn gốc khác nhau . Lưu ý cụ thể đó là lý do tại sao layoutSubviews:được gọi bất cứ khi nào UIScrollView cuộn, vì nó thực hiện cuộn bằng cách thay đổi nguồn gốc giới hạn của nó.
  • Thay đổi khung sẽ chỉ thay đổi giới hạn nếu kích thước đã thay đổi, vì đây là điều duy nhất được truyền đến thuộc tính giới hạn.
  • Một sự thay đổi trong giới hạn của một khung nhìn chưa có trong hệ thống phân cấp khung nhìn sẽ dẫn đến một cuộc gọi đến layoutSubviews: khi cuối cùng khung nhìn được thêm vào một cấu trúc phân cấp khung nhìn .
  • Và chỉ để hoàn thiện: các trình kích hoạt này không trực tiếp gọi layoutSubview, mà là gọi setNeedsLayout, mà đặt / tăng cờ. Mỗi lần lặp của vòng lặp chạy, đối với tất cả các khung nhìn trong hệ thống phân cấp khung nhìn , cờ này được chọn. Đối với mỗi chế độ xem cờ được tìm thấy được nâng lên, layoutSubviews:được gọi trên đó và cờ được đặt lại. Lượt xem cao hơn lên thứ bậc sẽ được kiểm tra / gọi đầu tiên.

5
Không thể đưa ra câu trả lời này đủ. nó nên là câu trả lời hàng đầu Ba quy tắc được đưa ra là tất cả những gì bạn cần. Tôi chưa bao giờ gặp bất kỳ bố cụcSubview hành vi nào mà các quy tắc này không mô tả hoàn hảo.
Pärserk

1
Tôi không nghĩ layoutSubview được gọi khi giới hạn của một khung nhìn trực tiếp được thay đổi. Tôi nghĩ rằng việc gọi layoutSubview chỉ là một tác dụng phụ của thử nghiệm của bạn. Trong một số trường hợp layoutSubview sẽ không được gọi khi giới hạn lượt xem thay đổi. Vui lòng kiểm tra câu trả lời của Frogcjn, bởi vì anwser của anh ấy dựa trên tài liệu của Apple thay vì chỉ là các thử nghiệm.
Simon Backx

19

https://developer.apple.com/lvern/prerelease/tvos/documentation/WindowsViews/Conceptual/ViewPG_iPhoneOS/CreatingViews/CreatingViews.html#//apple_Vf/doc/uid/TP40009503-CH5

Thay đổi bố cục có thể xảy ra bất cứ khi nào bất kỳ sự kiện nào sau đây xảy ra trong chế độ xem:

a. Kích thước của hình chữ nhật giới hạn của khung nhìn thay đổi.
b. Một sự thay đổi hướng giao diện xảy ra, thường gây ra thay đổi trong hình chữ nhật giới hạn của khung nhìn gốc.
c. Tập hợp các lớp con Core Animation được liên kết với thay đổi lớp của khung nhìn và yêu cầu bố cục.
d. Ứng dụng của bạn buộc bố cục xảy ra bằng cách gọi  setNeedsLayout hoặc  layoutIfNeeded phương thức của một khung nhìn.
e. Ứng dụng của bạn buộc bố cục bằng cách gọi  setNeedsLayout phương thức của đối tượng lớp bên dưới của khung nhìn.


Ngoài ra, điều quan trọng cần chỉ ra là không có sự kiện nào trong số đó được gọi khi chế độ xem chưa được thêm vào ngăn xếp chế độ xem. Bạn bao gồm từ này với từ "có thể", nhưng cụ thể nó được gọi trong chu kỳ có sẵn tiếp theo của luồng chính của ứng dụng khi nó được đánh dấu là cần bố trí bởi một trong những sự kiện đó.
dùng1122069

13

Một số điểm trong câu trả lời của BadPirate chỉ đúng một phần:

  1. Đối với addSubViewđiểm

    addSubview làm cho layoutSubview được gọi trên chế độ xem được thêm vào, chế độ xem được thêm vào (chế độ xem mục tiêu) và tất cả các lần xem xét của mục tiêu.

    Nó phụ thuộc vào mặt nạ tự động (khung nhìn đích) của khung nhìn. Nếu nó có mặt nạ tự động BẬT, layoutSubview sẽ được gọi trên mỗi mặt nạ addSubview. Nếu nó không có mặt nạ tự động kích thước thì layoutSubview sẽ chỉ được gọi khi kích thước khung hình của khung nhìn (mục tiêu) thay đổi.

    Ví dụ: nếu bạn đã tạo UIView theo chương trình (mặc định nó không có mặt nạ tự động kích hoạt), thì LayoutSubview sẽ chỉ được gọi khi khung UIView thay đổi không phải trên mỗi addSubview.

    Chính nhờ kỹ thuật này mà hiệu năng của ứng dụng cũng tăng lên.

  2. Đối với điểm xoay thiết bị

    Xoay thiết bị chỉ gọi layoutSubview trên chế độ xem chính (chế độ xem phản hồi của chế độ xem chính)

    Điều này chỉ có thể đúng khi VC của bạn nằm trong hệ thống phân cấp của VC (root at window.rootViewController), đây là trường hợp phổ biến nhất. Trong iOS 5, nếu bạn tạo một VC, nhưng nó không được thêm vào bất kỳ VC nào khác, thì VC này sẽ không được chú ý khi xoay thiết bị. Do đó, chế độ xem của nó sẽ không được chú ý bằng cách gọi layoutSubview.


9

Tôi đã theo dõi giải pháp để nhấn mạnh rằng Trình tạo giao diện không thể thay đổi lò xo trên chế độ xem có bật các thành phần màn hình mô phỏng (thanh trạng thái, v.v.). Vì các lò xo đã tắt cho chế độ xem chính, chế độ xem đó không thể thay đổi kích thước và do đó được cuộn xuống toàn bộ khi thanh trong cuộc gọi xuất hiện.

Tắt các tính năng mô phỏng, sau đó thay đổi kích thước chế độ xem và đặt lò xo chính xác khiến hoạt ảnh xảy ra và phương thức của tôi được gọi.

Một vấn đề nữa trong việc gỡ lỗi này là trình giả lập thoát khỏi ứng dụng khi trạng thái trong cuộc gọi được chuyển qua menu. Thoát ứng dụng = không có trình gỡ lỗi.


Bạn đang nói rằng layoutSubview được gọi khi khung nhìn được thay đổi kích thước? Tôi luôn cho rằng nó không phải ...
Andrey Tarantsov

Nó là. Khi một khung nhìn được thay đổi kích thước, nó cần phải làm một cái gì đó với các khung nhìn của nó. Nếu bạn không cung cấp thì nó sẽ tự động di chuyển chúng bằng lò xo, thanh chống, v.v.
Steve Weller

8

gọi [self.view setNeedsLayout]; trong viewControll làm cho nó gọi viewDidLayoutSubview


5

bạn đã nhìn vào layout IfNeeded chưa?

Đoạn tài liệu dưới đây. Hoạt hình có hoạt động không nếu bạn gọi phương thức này một cách rõ ràng trong khi hoạt hình?

layout IfNeeded Đặt ra các cuộc phỏng vấn nếu cần.

- (void)layoutIfNeeded

Thảo luận Sử dụng phương pháp này để buộc bố cục của các cuộc phỏng vấn trước khi vẽ.

Tính khả dụng Có sẵn trong iPhone OS 2.0 trở lên.


2

Khi di chuyển ứng dụng OpenGL từ SDK 3 sang 4, layoutSubview không được gọi nữa. Sau rất nhiều thử nghiệm và lỗi, cuối cùng tôi đã mở MainWindow.xib, chọn đối tượng Window, trong thanh tra chọn tab Thuộc tính cửa sổ (ngoài cùng bên trái) và chọn "Hiển thị khi khởi chạy". Có vẻ như trong SDK 3, nó vẫn được sử dụng để thực hiện cuộc gọi layoutSubViews, nhưng không phải trong 4.

6 giờ thất vọng chấm dứt.


Bạn đã làm chìa khóa cửa sổ? Nếu không, điều này có thể khiến tất cả những điều thú vị không xảy ra.
Steve Weller

-2

Một trường hợp khá mơ hồ nhưng có khả năng quan trọng khi layoutSubviewskhông bao giờ được gọi là:

import UIKit

class View: UIView {

    override class var layerClass: AnyClass { return Layer.self }

    class Layer: CALayer {
        override func layoutSublayers() {
            // if we don't call super.layoutSublayers()...
            print(type(of: self), #function)
        }
    }

    override func layoutSubviews() {
        // ... this method never gets called by the OS!
        print(type(of: self), #function)
    }
}

let view = View(frame: CGRect(x: 0, y: 0, width: 100, height: 100))

1
Tại sao câu trả lời này ở đây?
Dominik Bucher
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.