Storyboard, Code, Tips và một vài Gotchas
Các câu trả lời khác chỉ tốt nhưng câu trả lời này nêu bật một vài vấn đề khá quan trọng về các ràng buộc hoạt hình bằng một ví dụ gần đây. Tôi đã trải qua rất nhiều biến thể trước khi tôi nhận ra những điều sau đây:
Tạo các ràng buộc bạn muốn nhắm mục tiêu vào các biến Class để giữ tham chiếu mạnh. Trong Swift tôi đã sử dụng các biến lười biếng:
lazy var centerYInflection:NSLayoutConstraint = {
let temp = self.view.constraints.filter({ $0.firstItem is MNGStarRating }).filter ( { $0.secondItem is UIWebView }).filter({ $0.firstAttribute == .CenterY }).first
return temp!
}()
Sau một số thử nghiệm, tôi đã lưu ý rằng một PHẢI có được các ràng buộc từ chế độ xem TRÊN (hay còn gọi là giám sát) hai chế độ xem các ràng buộc được xác định. Trong ví dụ dưới đây (cả MNGStarRating và UIWebView là hai loại mục tôi đang tạo ra một ràng buộc giữa và chúng là các cuộc phỏng vấn trong self.view).
Lọc chuỗi
Tôi tận dụng phương pháp lọc của Swift để phân tách ràng buộc mong muốn sẽ đóng vai trò là điểm uốn. Người ta cũng có thể trở nên phức tạp hơn nhiều nhưng bộ lọc thực hiện công việc tốt ở đây.
Hạn chế hoạt hình bằng Swift
Nota Bene - Ví dụ này là giải pháp bảng phân cảnh / mã và giả sử người ta đã thực hiện các ràng buộc mặc định trong bảng phân cảnh. Sau đó, người ta có thể làm động các thay đổi bằng cách sử dụng mã.
Giả sử bạn tạo một thuộc tính để lọc với các tiêu chí chính xác và đến một điểm uốn cụ thể cho hoạt hình của bạn (tất nhiên bạn cũng có thể lọc một mảng và lặp qua nếu bạn cần nhiều ràng buộc):
lazy var centerYInflection:NSLayoutConstraint = {
let temp = self.view.constraints.filter({ $0.firstItem is MNGStarRating }).filter ( { $0.secondItem is UIWebView }).filter({ $0.firstAttribute == .CenterY }).first
return temp!
}()
....
Thỉnh thoảng ...
@IBAction func toggleRatingView (sender:AnyObject){
let aPointAboveScene = -(max(UIScreen.mainScreen().bounds.width,UIScreen.mainScreen().bounds.height) * 2.0)
self.view.layoutIfNeeded()
//Use any animation you want, I like the bounce in springVelocity...
UIView.animateWithDuration(1.0, delay: 0.0, usingSpringWithDamping: 0.3, initialSpringVelocity: 0.75, options: [.CurveEaseOut], animations: { () -> Void in
//I use the frames to determine if the view is on-screen
if CGRectContainsRect(self.view.frame, self.ratingView.frame) {
//in frame ~ animate away
//I play a sound to give the animation some life
self.centerYInflection.constant = aPointAboveScene
self.centerYInflection.priority = UILayoutPriority(950)
} else {
//I play a different sound just to keep the user engaged
//out of frame ~ animate into scene
self.centerYInflection.constant = 0
self.centerYInflection.priority = UILayoutPriority(950)
self.view.setNeedsLayout()
self.view.layoutIfNeeded()
}) { (success) -> Void in
//do something else
}
}
}
Nhiều lượt sai
Những ghi chú này thực sự là một tập hợp các mẹo mà tôi đã viết cho chính mình. Tôi đã làm tất cả các cá nhân và đau đớn. Hy vọng hướng dẫn này có thể tha cho người khác.
Xem ra cho zP vị trí. Đôi khi, khi không có gì rõ ràng xảy ra, bạn nên ẩn một số chế độ xem khác hoặc sử dụng trình gỡ lỗi chế độ xem để xác định vị trí xem hoạt hình của mình. Tôi thậm chí đã tìm thấy các trường hợp thuộc tính Thời gian chạy do Người dùng Xác định bị mất trong xml của bảng phân cảnh và dẫn đến chế độ xem hoạt hình được che (trong khi làm việc).
Luôn dành một phút để đọc tài liệu (mới và cũ), Trợ giúp nhanh và tiêu đề. Apple tiếp tục thực hiện nhiều thay đổi để quản lý tốt hơn các ràng buộc AutoLayout (xem chế độ xem ngăn xếp). Hoặc ít nhất là AutoLayout Cookbook . Hãy nhớ rằng đôi khi các giải pháp tốt nhất là trong các tài liệu / video cũ hơn.
Chơi xung quanh với các giá trị trong hình động và xem xét sử dụng các biến thể animateWithDuration khác.
Không mã hóa các giá trị bố cục cụ thể làm tiêu chí để xác định thay đổi cho các hằng số khác, thay vào đó sử dụng các giá trị cho phép bạn xác định vị trí của chế độ xem. CGRectContainsRect
là một ví dụ
- Nếu cần, đừng ngần ngại sử dụng lề bố cục được liên kết với chế độ xem tham gia định nghĩa ràng buộc
let viewMargins = self.webview.layoutMarginsGuide
: là ví dụ
- Đừng làm công việc mà bạn không phải làm, tất cả các chế độ xem có ràng buộc trên bảng phân cảnh đều có các ràng buộc được gắn vào thuộc tính self.viewName.constraint
- Thay đổi mức độ ưu tiên của bạn cho bất kỳ ràng buộc nào dưới 1000. Tôi đặt mức của tôi thành 250 (thấp) hoặc 750 (cao) trên bảng phân cảnh; (nếu bạn cố gắng thay đổi mức ưu tiên 1000 thành bất kỳ thứ gì trong mã thì ứng dụng sẽ sập vì 1000 là bắt buộc)
- Cân nhắc không ngay lập tức cố gắng sử dụng activConstraint và hủy kích hoạtConstraint (họ có vị trí của họ nhưng khi vừa học hoặc nếu bạn đang sử dụng bảng phân cảnh bằng cách sử dụng những thứ này có thể có nghĩa là bạn làm quá nhiều ~ họ có một vị trí như được thấy bên dưới)
- Cân nhắc không sử dụng addConstraint / removeConstraint trừ khi bạn thực sự thêm một ràng buộc mới trong mã. Tôi thấy rằng hầu hết các lần tôi bố trí các khung nhìn trong bảng phân cảnh với các ràng buộc mong muốn (đặt màn hình ngoài màn hình), sau đó bằng mã, tôi làm động các ràng buộc được tạo trước đó trong bảng phân cảnh để di chuyển khung nhìn xung quanh.
- Tôi đã dành rất nhiều thời gian lãng phí để xây dựng các ràng buộc với lớp và lớp con NSAnchorLayout mới. Chúng chỉ hoạt động tốt nhưng tôi phải mất một thời gian để nhận ra rằng tất cả các ràng buộc mà tôi cần đã tồn tại trong bảng phân cảnh. Nếu bạn xây dựng các ràng buộc trong mã thì chắc chắn sử dụng phương pháp này để tổng hợp các ràng buộc của bạn:
Mẫu giải pháp nhanh để TRÁNH khi sử dụng Bảng phân cảnh
private var _nc:[NSLayoutConstraint] = []
lazy var newConstraints:[NSLayoutConstraint] = {
if !(self._nc.isEmpty) {
return self._nc
}
let viewMargins = self.webview.layoutMarginsGuide
let minimumScreenWidth = min(UIScreen.mainScreen().bounds.width,UIScreen.mainScreen().bounds.height)
let centerY = self.ratingView.centerYAnchor.constraintEqualToAnchor(self.webview.centerYAnchor)
centerY.constant = -1000.0
centerY.priority = (950)
let centerX = self.ratingView.centerXAnchor.constraintEqualToAnchor(self.webview.centerXAnchor)
centerX.priority = (950)
if let buttonConstraints = self.originalRatingViewConstraints?.filter({
($0.firstItem is UIButton || $0.secondItem is UIButton )
}) {
self._nc.appendContentsOf(buttonConstraints)
}
self._nc.append( centerY)
self._nc.append( centerX)
self._nc.append (self.ratingView.leadingAnchor.constraintEqualToAnchor(viewMargins.leadingAnchor, constant: 10.0))
self._nc.append (self.ratingView.trailingAnchor.constraintEqualToAnchor(viewMargins.trailingAnchor, constant: 10.0))
self._nc.append (self.ratingView.widthAnchor.constraintEqualToConstant((minimumScreenWidth - 20.0)))
self._nc.append (self.ratingView.heightAnchor.constraintEqualToConstant(200.0))
return self._nc
}()
Nếu bạn quên một trong những mẹo này hoặc những mẹo đơn giản hơn như thêm bố cục vào đâu Nếu cần, rất có thể sẽ không có gì xảy ra: Trong trường hợp đó bạn có thể có một giải pháp nướng một nửa như thế này:
NB - Dành một chút thời gian để đọc Phần AutoLayout bên dưới và hướng dẫn ban đầu. Có một cách để sử dụng các kỹ thuật này để bổ sung cho Dynamic Animators của bạn.
UIView.animateWithDuration(1.0, delay: 0.0, usingSpringWithDamping: 0.3, initialSpringVelocity: 1.0, options: [.CurveEaseOut], animations: { () -> Void in
//
if self.starTopInflectionPoint.constant < 0 {
//-3000
//offscreen
self.starTopInflectionPoint.constant = self.navigationController?.navigationBar.bounds.height ?? 0
self.changeConstraintPriority([self.starTopInflectionPoint], value: UILayoutPriority(950), forView: self.ratingView)
} else {
self.starTopInflectionPoint.constant = -3000
self.changeConstraintPriority([self.starTopInflectionPoint], value: UILayoutPriority(950), forView: self.ratingView)
}
}) { (success) -> Void in
//do something else
}
}
Đoạn trích từ Hướng dẫn tự động thanh toán (lưu ý đoạn mã thứ hai là để sử dụng OS X). BTW - Điều này không còn trong hướng dẫn hiện tại như tôi có thể thấy. Các kỹ thuật ưa thích tiếp tục phát triển.
Thay đổi hoạt hình được thực hiện bởi bố cục tự động
Nếu bạn cần toàn quyền kiểm soát các thay đổi hoạt hình được thực hiện bởi Bố cục tự động, bạn phải thực hiện các ràng buộc thay đổi theo chương trình. Khái niệm cơ bản là giống nhau cho cả iOS và OS X, nhưng có một vài khác biệt nhỏ.
Trong một ứng dụng iOS, mã của bạn sẽ trông giống như sau:
[containerView layoutIfNeeded]; // Ensures that all pending layout operations have been completed
[UIView animateWithDuration:1.0 animations:^{
// Make all constraint changes here
[containerView layoutIfNeeded]; // Forces the layout of the subtree animation block and then captures all of the frame changes
}];
Trong OS X, sử dụng mã sau đây khi sử dụng hình động dựa trên lớp:
[containterView layoutSubtreeIfNeeded];
[NSAnimationContext runAnimationGroup:^(NSAnimationContext *context) {
[context setAllowsImplicitAnimation: YES];
// Make all constraint changes here
[containerView layoutSubtreeIfNeeded];
}];
Khi bạn không sử dụng hoạt ảnh dựa trên lớp, bạn phải tạo hiệu ứng hằng số bằng cách sử dụng trình hoạt hình của ràng buộc:
[[constraint animator] setConstant:42];
Đối với những người tìm hiểu trực quan tốt hơn hãy xem video đầu tiên này từ Apple .
Chú ý hơn
Thông thường trong tài liệu có những ghi chú nhỏ hoặc những đoạn mã dẫn đến những ý tưởng lớn hơn. Ví dụ, gắn các ràng buộc bố cục tự động cho các hoạt hình động là một ý tưởng lớn.
Chúc may mắn và có thể lực lượng sẽ được với bạn.