Làm cách nào để có được chiều rộng và chiều cao hiện tại của chế độ xem khi sử dụng các ràng buộc tự động thanh toán?


108

Tôi không nói về thuộc tính khung, bởi vì từ đó bạn chỉ có thể nhận được kích thước của chế độ xem trong xib. Tôi đang nói về thời điểm chế độ xem được thay đổi kích thước do các hạn chế của nó (có thể sau khi xoay hoặc phản ứng với một sự kiện). Có cách nào để có được chiều rộng và chiều cao hiện tại của nó không?

Tôi đã thử lặp lại các ràng buộc của nó để tìm các ràng buộc về chiều rộng và chiều cao, nhưng điều đó không rõ ràng lắm và không thành công khi có các ràng buộc nội tại (vì tôi không thể phân biệt giữa hai ràng buộc này). Ngoài ra, điều đó chỉ hoạt động nếu chúng thực sự có các ràng buộc về chiều rộng và chiều cao, điều này sẽ không hoạt động nếu chúng dựa vào các ràng buộc khác để thay đổi kích thước.

Tại sao điều này lại khó khăn đối với tôi. ARG!


Tôi đã nghĩ rằng giới hạn của chế độ xem tại bất kỳ thời điểm cụ thể nào đều giữ chiều rộng và chiều cao hiện tại của nó. Đó là những gì được điều chỉnh trong quá trình bố trí.
Phillip Mills

Hmm, tôi chưa bao giờ nghĩ sẽ kiểm tra giới hạn. Tôi se lam điêu đo ngay.
yuf

Câu trả lời:


246

Câu trả lời là [view layoutIfNeeded].

Đây là lý do tại sao:

Bạn vẫn nhận được chiều rộng và chiều cao hiện tại của chế độ xem bằng cách kiểm tra view.bounds.size.widthview.bounds.size.height(hoặc khung, tương đương trừ khi bạn đang chơi với view.transform).

Nếu những gì bạn muốn là chiều rộng và chiều cao được ngụ ý bởi các ràng buộc hiện có của bạn, câu trả lời là không phải kiểm tra các ràng buộc theo cách thủ công, vì điều đó sẽ yêu cầu bạn triển khai lại toàn bộ logic giải quyết ràng buộc của hệ thống bố cục tự động để giải thích những điều đó những ràng buộc. Thay vào đó, những gì bạn nên làm chỉ là yêu cầu bố cục tự động cập nhật bố cục đó , để nó giải quyết các ràng buộc và cập nhật giá trị của view.bounds với giải pháp chính xác, sau đó bạn kiểm tra view.bounds.

Làm thế nào để bạn yêu cầu bố cục tự động để cập nhật bố cục? Gọi [view setNeedsLayout]nếu bạn muốn bố cục tự động để cập nhật bố cục vào lượt tiếp theo của vòng chạy.

Tuy nhiên, nếu bạn muốn nó cập nhật bố cục ngay lập tức, để bạn có thể truy cập ngay vào giá trị giới hạn mới sau này trong hàm hiện tại của mình hoặc tại một thời điểm khác trước khi đến lượt của vòng chạy, thì bạn cần gọi [view setNeedsLayout][view layoutIfNeeded].

Bạn đã hỏi câu hỏi thứ hai: "làm thế nào tôi có thể thay đổi giới hạn chiều cao / chiều rộng nếu tôi không có tham chiếu trực tiếp đến nó?".

Nếu bạn tạo ràng buộc trong IB, giải pháp tốt nhất là tạo IBOutlet trong bộ điều khiển chế độ xem hoặc chế độ xem của bạn để bạn có tham chiếu trực tiếp đến nó. Nếu bạn đã tạo ràng buộc trong mã, thì bạn nên giữ một tham chiếu trong thuộc tính yếu bên trong tại thời điểm bạn tạo nó. Nếu ai đó khác đã tạo ràng buộc, thì bạn cần tìm nó bằng cách kiểm tra việc kiểm tra thuộc tính view.constraints trên chế độ xem, và có thể là toàn bộ hệ thống phân cấp chế độ xem và thực hiện logic tìm ra NSLayoutConstraint quan trọng. Đây có lẽ là một cách sai lầm, vì nó cũng yêu cầu bạn xác định ràng buộc cụ thể nào xác định kích thước giới hạn, khi không được đảm bảo là một câu trả lời đơn giản cho câu hỏi đó. Giá trị giới hạn cuối cùng có thể là giải pháp cho một hệ thống rất phức tạp gồm nhiều ràng buộc,


16
Một câu trả lời xuất sắc và cũng được giải thích rõ ràng. Đã cứu tôi sau một hoặc hai giờ nhổ tóc.
Ben Kreeger

2
layoutIfNeededlà tuyệt vời, và sẽ làm cho khung có sẵn ngay lập tức. Tuy nhiên, nó cũng sẽ buộc hiển thị các ràng buộc trên tất cả các khung nhìn trong cây con của khung nhìn mà nó được gọi. Ví dụ: nếu bạn đang thêm các chế độ xem theo chương trình và gọi layoutIfNeededtất cả chúng trong quy trình đệ quy của mình, bạn có thể thấy rằng hệ thống phân cấp chế độ xem của bạn hiển thị rất chậm. (Tôi đã học điều này một cách khó khăn) Như đã đề cập trong câu trả lời tuyệt vời, 'setNeedsLayout` hiệu quả hơn và sẽ làm cho khung có sẵn trong lần chuyển bố cục tiếp theo, điều này lý tưởng xảy ra trong khoảng 1/60 giây.
shmim

1
Tôi biết điều này là cũ, nhưng bạn gọi phương pháp này ở đâu? Trong initWithCoder?
MayNotBe

4
Tại sao buộc bố cục chỉ để có được giới hạn? Điều này có vẻ không hiệu quả và với một giao diện phức tạp, điều này có thể sẽ tốn kém. Thay vào đó, hãy truy cập các giới hạn mới nhất từ ​​viewDidLayoutSubviews hoặc thậm chí viewWillLayoutSubviews và để cacao xử lý thời gian bố cục của riêng nó. Đặt thuộc tính nếu bạn cần truy cập giá trị hoặc cờ để tránh nhiều lệnh gọi nếu đó là sự cố.
smileBot

1
Có, bạn chỉ cần buộc bố trí nếu bạn cần quyền truy cập vào giá trị được tính toán bố cục tự động trước khi đến lượt của vòng lặp chạy - ví dụ: trong phạm vi của lệnh gọi hàm hiện tại.
algal

8

Tôi đã gặp sự cố tương tự trong đó tôi cần thêm đường viền trên và dưới vào một đường viền UITableViewthay đổi kích thước dựa trên thiết lập các ràng buộc của nó trong UIStoryboard. Tôi đã có thể truy cập các ràng buộc được cập nhật với - (void)viewDidLayoutSubviews. Điều này rất hữu ích để bạn không cần phải phân lớp một dạng xem và ghi đè phương thức bố cục của nó.

/*** SET TOP AND BOTTOM BORDERS ON TABLE VIEW ***/
- (void)addBorders
{
    CALayer *topBorder           = [CALayer layer];
    topBorder.frame              = CGRectMake(0.0f, self.tableView.frame.origin.y, 320.0f, 0.5f);
    topBorder.backgroundColor    = [UIColor redColor].CGColor;

    CALayer *bottomBorder        = [CALayer layer];
    bottomBorder.frame           = CGRectMake(0.0f, (self.tableView.frame.origin.y + self.tableView.frame.size.height), 320.0f, 0.5f);
    bottomBorder.backgroundColor = [UIColor redColor].CGColor;

    [self.view.layer addSublayer:topBorder];
    [self.view.layer addSublayer:bottomBorder];
}

/*** GET AUTORESIZED FRAME DIMENSIONS ***/
- (void)viewDidLayoutSubviews{
    [self addBorders];
}

Không gọi phương thức từ viewDidLayoutSubviewphương thức, chỉ đường viền trên cùng được vẽ chính xác, vì đường viền dưới cùng nằm ở đâu đó ngoài màn hình.


3
viewDidLayoutSubviews được gọi một vài lần, bạn đang tạo hàng tấn lớp ... Bạn cần thêm một số kiểm tra sự tồn tại trước khi thêm một lớp khác .
Kalzem

Bình luận vài năm muộn ... bạn cũng nên gọi phương thức này trên lớp cha khi trọng trước bất cứ điều gì khác:[super viewDidLayoutSubviews];
Alejandro Iván

5

Đối với những người có thể vẫn phải đối mặt với những vấn đề như vậy, đặc biệt là với TableviewCell.

Chỉ cần ghi đè phương thức:

-(void)layoutSubviews
{
//your code here like drawing a shadow
}

Trong trường hợp UITableViewCell hoặc UICollectionViewCell, hãy tạo một lớp con của ô và ghi đè cùng một phương thức:

-(void)layoutSubviews
{
//your code here like drawing a shadow
}

đây là cách duy nhất tôi có thể nhận được kích thước cuối cùng thực sự của quan điểm của tôi
Benoit Jadinon

Đây là cách duy nhất để tôi có thể có được kích thước cuối cùng cho bất kỳ chế độ xem bị hạn chế nào của mình bởi vì trước đó tôi đã cố gắng đưa chúng lên AwakeFromNib đã không cho các lần xem phụ thời gian để thực sự thay đổi kích thước trước. Cảm ơn bạn!
Azin Mehrnoosh

3

Sử dụng -(void)viewWillAppear:(BOOL)animatedvà gọi [self.view layoutIfNeeded];- Nó hoạt động tôi đã thử.

bởi vì nếu bạn sử dụng -(void)viewDidLayoutSubviewsnó sẽ hoạt động chắc chắn nhưng phương pháp này được gọi mỗi khi giao diện người dùng của bạn yêu cầu cập nhật / thay đổi. Sẽ khó quản lý. Phím mềm là bạn sử dụng một biến bool để tránh lặp lại các cuộc gọi như vậy. sử dụng tốt hơn viewWillAppear. Nhớ viewWillAppearcũng sẽ được gọi nếu chế độ xem được tải trở lại (mà không cần phân bổ lại).


2

Khung vẫn còn giá trị. Cuối cùng, khung nhìn sử dụng thuộc tính khung của nó để bố trí chính nó. Nó tính toán khung đó dựa trên tất cả các ràng buộc. Các ràng buộc chỉ được sử dụng cho bố cục ban đầu (và bất kỳ thời gian nào layoutSubviews được gọi trên một khung nhìn như sau một vòng quay). Sau đó, thông tin vị trí nằm trong thuộc tính khung. Hay bạn đang thấy khác?


1
Tôi đang thấy khác. Các giá trị khung không thay đổi, ngay cả sau khi thay đổi trực tiếp các ràng buộc chiều rộng / chiều cao (bằng cách thay đổi hằng số của chúng. Tôi có IBOutlet cho chúng trong xib)
yuf 19/11/12

Vấn đề của tôi là duy nhất vì tôi thay đổi ràng buộc, sau đó cần sử dụng khung trong cùng một chức năng. Gọi setNeedsLayout không cập nhật nó ngay lập tức. Tôi đoán tôi cần phải đợi bố cục trước rồi mới sử dụng khung. Câu hỏi thứ hai của tôi là, làm thế nào tôi có thể thay đổi giới hạn chiều cao / chiều rộng nếu tôi không có tham chiếu trực tiếp đến nó?
yuf

1
Sau đó, bạn nên gửi đi.async, và nó sẽ cho phép bạn trì hoãn mã của mình cho đến khung tiếp theo. Đối với phần thứ hai ... Tôi không biết ... bạn sẽ phải lặp lại tất cả các ràng buộc và tìm kiếm cái nào có thể áp dụng ... IBOutlet dễ dàng hơn nhiều.
borrrden

Đúng cho IBOutlets, nhưng tôi muốn viết một hàm trong một danh mục cho UIView mà tôi sẽ không có IB để làm việc cùng.
yuf
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.