[EDIT: Cảnh báo: Toàn bộ cuộc thảo luận tiếp theo sẽ có thể bị lỗi thời hoặc ít nhất là được giảm nhẹ bởi iOS 8, điều này có thể không còn gây ra lỗi khi kích hoạt bố cục tại thời điểm áp dụng chuyển đổi chế độ xem.]
Tự động thanh toán so với Xem chuyển đổi
Autolayout hoàn toàn không chơi với các biến đổi chế độ xem. Lý do, theo như tôi có thể nhận ra, là bạn không nên lộn xộn với khung của chế độ xem có biến đổi (không phải là biến đổi nhận dạng mặc định) - nhưng đó chính xác là những gì tự động thanh toán. Cách thức tự động thanh toán hoạt động là trong layoutSubviews
thời gian chạy xuất hiện thông qua tất cả các ràng buộc và thiết lập các khung của tất cả các chế độ xem tương ứng.
Nói cách khác, các ràng buộc không phải là phép thuật; chúng chỉ là một danh sách việc cần làm. layoutSubviews
là nơi danh sách việc cần làm được thực hiện. Và nó làm điều đó bằng cách thiết lập khung.
Tôi không thể giúp về vấn đề này như là một lỗi. Nếu tôi áp dụng biến đổi này cho một khung nhìn:
v.transform = CGAffineTransformMakeScale(0.5,0.5);
Tôi hy vọng sẽ thấy chế độ xem xuất hiện với tâm của nó ở cùng một vị trí như trước và ở một nửa kích thước. Nhưng tùy thuộc vào các ràng buộc của nó, đó có thể không phải là những gì tôi thấy.
[Trên thực tế, có một bất ngờ thứ hai ở đây: áp dụng chuyển đổi cho bố cục kích hoạt chế độ xem ngay lập tức. Đây dường như là một lỗi khác. Hoặc có lẽ đó là trung tâm của lỗi đầu tiên. Những gì tôi mong đợi là có thể thoát khỏi một biến đổi ít nhất cho đến khi bố trí thời gian, ví dụ như thiết bị được xoay - giống như tôi có thể thoát khỏi hoạt hình khung cho đến khi bố trí thời gian. Nhưng trên thực tế thời gian bố trí là ngay lập tức, có vẻ như chỉ là sai.]
Giải pháp 1: Không ràng buộc
Một giải pháp hiện tại là, nếu tôi sẽ áp dụng một biến đổi bán vĩnh viễn cho một khung nhìn (và không chỉ đơn giản là vẫy nó tạm thời bằng cách nào đó), để loại bỏ tất cả các ràng buộc ảnh hưởng đến nó. Thật không may, điều này thường làm cho chế độ xem biến mất khỏi màn hình, vì tính năng tự động thanh toán vẫn diễn ra và hiện tại không có ràng buộc nào cho chúng tôi biết nơi để đặt chế độ xem. Vì vậy, ngoài việc loại bỏ các ràng buộc, tôi đặt chế độ xem translatesAutoresizingMaskIntoConstraints
thành CÓ. Chế độ xem hiện hoạt động theo cách cũ, không bị ảnh hưởng bởi tính năng tự động thanh toán. (Nó đang bị ảnh hưởng bởi autolayout, rõ ràng, nhưng những hạn chế mặt nạ autoresizing tiềm ẩn gây ra hành vi của nó là giống như nó đã được trước autolayout.)
Giải pháp 2: Chỉ sử dụng các ràng buộc phù hợp
Nếu điều đó có vẻ hơi quyết liệt, một giải pháp khác là đặt các ràng buộc hoạt động chính xác với một biến đổi dự định. Ví dụ, nếu một khung nhìn có kích thước hoàn toàn bằng chiều rộng và chiều cao cố định bên trong và được định vị hoàn toàn bởi trung tâm của nó, ví dụ, biến đổi tỷ lệ của tôi sẽ hoạt động như tôi mong đợi. Trong mã này, tôi loại bỏ các ràng buộc hiện có trên một khung nhìn phụ ( otherView
) và thay thế chúng bằng bốn ràng buộc đó, tạo cho nó một chiều rộng và chiều cao cố định và ghim hoàn toàn vào trung tâm của nó. Sau đó, biến đổi quy mô của tôi hoạt động:
NSMutableArray* cons = [NSMutableArray array];
for (NSLayoutConstraint* con in self.view.constraints)
if (con.firstItem == self.otherView || con.secondItem == self.otherView)
[cons addObject:con];
[self.view removeConstraints:cons];
[self.otherView removeConstraints:self.otherView.constraints];
[self.view addConstraint:
[NSLayoutConstraint constraintWithItem:self.otherView attribute:NSLayoutAttributeCenterX relatedBy:0 toItem:self.view attribute:NSLayoutAttributeLeft multiplier:1 constant:self.otherView.center.x]];
[self.view addConstraint:
[NSLayoutConstraint constraintWithItem:self.otherView attribute:NSLayoutAttributeCenterY relatedBy:0 toItem:self.view attribute:NSLayoutAttributeTop multiplier:1 constant:self.otherView.center.y]];
[self.otherView addConstraint:
[NSLayoutConstraint constraintWithItem:self.otherView attribute:NSLayoutAttributeWidth relatedBy:0 toItem:nil attribute:0 multiplier:1 constant:self.otherView.bounds.size.width]];
[self.otherView addConstraint:
[NSLayoutConstraint constraintWithItem:self.otherView attribute:NSLayoutAttributeHeight relatedBy:0 toItem:nil attribute:0 multiplier:1 constant:self.otherView.bounds.size.height]];
Kết quả cuối cùng là nếu bạn không có ràng buộc nào ảnh hưởng đến khung hình của chế độ xem, tự động thanh toán sẽ không chạm vào khung hình của chế độ xem - đó chỉ là những gì bạn có sau khi chuyển đổi có liên quan.
Giải pháp 3: Sử dụng một Subview
Vấn đề với cả hai giải pháp trên là chúng ta mất đi lợi ích của các ràng buộc để định vị quan điểm của mình. Vì vậy, đây là một giải pháp giải quyết điều đó. Bắt đầu với một chế độ xem vô hình có công việc chỉ hoạt động như một máy chủ lưu trữ và sử dụng các ràng buộc để định vị nó. Bên trong đó, đặt quan điểm thực sự như một cuộc phỏng vấn. Sử dụng các ràng buộc để định vị khung nhìn phụ trong chế độ xem máy chủ, nhưng giới hạn các ràng buộc đó đối với các ràng buộc sẽ không chống lại khi chúng tôi áp dụng một biến đổi.
Đây là một minh họa:
Chế độ xem màu trắng là chế độ xem máy chủ; bạn có nghĩa vụ phải giả vờ rằng nó minh bạch và do đó vô hình. Chế độ xem màu đỏ là khung nhìn phụ của nó, được định vị bằng cách ghim trung tâm của nó vào trung tâm của chế độ xem máy chủ. Bây giờ chúng ta có thể mở rộng và xoay chế độ xem màu đỏ xung quanh trung tâm của nó mà không gặp vấn đề gì, và thực sự hình minh họa cho thấy chúng ta đã làm như vậy:
self.otherView.transform = CGAffineTransformScale(self.otherView.transform, 0.5, 0.5);
self.otherView.transform = CGAffineTransformRotate(self.otherView.transform, M_PI/8.0);
Và trong khi đó, các ràng buộc trên giao diện máy chủ giữ nó ở đúng vị trí khi chúng ta xoay thiết bị.
Giải pháp 4: Thay vào đó, sử dụng chuyển đổi lớp
Thay vì chuyển đổi khung nhìn, hãy sử dụng các biến đổi lớp, không kích hoạt bố cục và do đó không gây ra xung đột ngay lập tức với các ràng buộc.
Ví dụ: hoạt hình xem "nhói" đơn giản này có thể bị phá vỡ trong chế độ tự động thanh toán:
[UIView animateWithDuration:0.3 delay:0
options:UIViewAnimationOptionAutoreverse
animations:^{
v.transform = CGAffineTransformMakeScale(1.1, 1.1);
} completion:^(BOOL finished) {
v.transform = CGAffineTransformIdentity;
}];
Mặc dù cuối cùng, không có thay đổi nào về kích thước của chế độ xem, chỉ cần đặt transform
bố cục nguyên nhân của nó xảy ra và các ràng buộc có thể khiến chế độ xem nhảy xung quanh. (Điều này có cảm thấy giống như lỗi hay không?) Nhưng nếu chúng ta làm điều tương tự với Core Animation (sử dụng CABasicAnimation và áp dụng hoạt hình cho lớp của khung nhìn), bố cục sẽ không xảy ra và nó hoạt động tốt:
CABasicAnimation* ba = [CABasicAnimation animationWithKeyPath:@"transform"];
ba.autoreverses = YES;
ba.duration = 0.3;
ba.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(1.1, 1.1, 1)];
[v.layer addAnimation:ba forKey:nil];
layerView
biết chiều rộng của nó? Là nó gắn bên phải của nó với một cái gì đó khác?