Làm cách nào để điều chỉnh điểm neo của CALayer, khi Giao diện tự động đang được sử dụng?


161

Lưu ý : Mọi thứ đã được chuyển từ khi câu hỏi này được hỏi; xem ở đây để có một cái nhìn tổng quan gần đây.


Trước khi bố trí tự động, bạn có thể thay đổi điểm neo của lớp của chế độ xem mà không di chuyển chế độ xem bằng cách lưu trữ khung, đặt điểm neo và khôi phục khung.

Trong một thế giới bố cục tự động, chúng tôi không đặt khung nữa, nhưng các ràng buộc dường như không phụ thuộc vào nhiệm vụ điều chỉnh vị trí của chế độ xem trở lại nơi chúng tôi muốn. Bạn có thể hack các ràng buộc để định vị lại chế độ xem của mình, nhưng khi xoay vòng hoặc các sự kiện thay đổi kích thước khác, chúng sẽ trở lại không hợp lệ.

Ý tưởng sáng sau đây không hoạt động vì nó tạo ra "Ghép nối các thuộc tính bố cục không hợp lệ (trái và chiều rộng)":

layerView.layer.anchorPoint = CGPointMake(1.0, 0.5);
// Some other size-related constraints here which all work fine...
[self.view addConstraint:
    [NSLayoutConstraint constraintWithItem:layerView
                                 attribute:NSLayoutAttributeLeft
                                 relatedBy:NSLayoutRelationEqual 
                                    toItem:layerView 
                                 attribute:NSLayoutAttributeWidth 
                                multiplier:0.5 
                                  constant:20.0]];

Ý định của tôi ở đây là đặt cạnh trái của layerView, khung nhìn với điểm neo được điều chỉnh, bằng một nửa chiều rộng của nó cộng với 20 (khoảng cách tôi muốn chèn từ cạnh trái của giám sát).

Có thể thay đổi điểm neo, mà không thay đổi vị trí của chế độ xem, trong chế độ xem được bố trí tự động không? Tôi có cần sử dụng các giá trị được mã hóa cứng và chỉnh sửa các ràng buộc trên mỗi vòng quay không? Tôi không hy vọng.

Tôi cần thay đổi điểm neo để khi tôi áp dụng biến đổi cho chế độ xem, tôi sẽ có được hiệu ứng hình ảnh chính xác.


Chỉ cần đưa ra những gì bạn có ở đây, dường như bạn sẽ kết thúc với bố cục mơ hồ dù sao bạn đã nhận được mã ở trên. Làm thế nào để layerViewbiế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?
John Estropia

Điều đó được bao phủ trong //Size-related constraints that work fine- chiều rộng và chiều cao của lớp xem được lấy từ giám sát.
jrturton

Câu trả lời:


361

[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 layoutSubviewsthờ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. layoutSubviewslà 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 translatesAutoresizingMaskIntoConstraintsthà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:

nhập mô tả hình ảnh ở đây

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 transformbố 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];

3
Biến đổi không phải là nguyên nhân gây ra sự cố cho tôi, chỉ cần thiết lập điểm neo mới. Mặc dù vậy, tôi sẽ cố gắng loại bỏ các ràng buộc và thử mặt nạ tự động hóa.
jrturton

1
Tôi không hiểu điều gì ở đây. Giải pháp4: Khi một biến đổi được áp dụng cho lớp sao lưu cũng biến đổi cũng ảnh hưởng đến tự động thanh toán, bởi vì thuộc tính biến đổi của chế độ xem đang đề cập đến biến đổi của lớp sao lưu
Luca Bartoletti

1
@Andy thì ngược lại, trên iOS 8, vấn đề đã hoàn toàn biến mất và toàn bộ câu trả lời của tôi là không cần thiết
matt

1
@Sunkas Tôi đã trả lời trong vấn đề của bạn.
Ben Sinclair

2
Trên iOS 8, chuyển đổi lớp cũng kích hoạt bố cục tự động
CarmeloS

41

Tôi đã có một Isuue tương tự và vừa nghe lại từ Nhóm Autolayout tại Apple. Họ đề nghị sử dụng phương pháp tiếp cận Container View, nhưng họ tạo một lớp con của UIView để ghi đè lên layoutSubview và áp dụng Mã bố cục tùy chỉnh ở đó - Nó hoạt động như một bùa mê

Tệp Tiêu đề trông giống như vậy để bạn có thể liên kết trực tiếp lựa chọn phụ của mình từ Trình tạo giao diện

#import <UIKit/UIKit.h>

@interface BugFixContainerView : UIView
@property(nonatomic,strong) IBOutlet UIImageView *knobImageView;
@end

và m File áp dụng Mã đặc biệt như thế:

#import "BugFixContainerView.h"

@implementation BugFixContainerView
- (void)layoutSubviews
{
    static CGPoint fixCenter = {0};
    [super layoutSubviews];
    if (CGPointEqualToPoint(fixCenter, CGPointZero)) {
        fixCenter = [self.knobImageView center];
    } else {
        self.knobImageView.center = fixCenter;
    }
}
@end

Như bạn có thể thấy, nó lấy điểm trung tâm của Chế độ xem khi được gọi lần đầu và sử dụng lại Vị trí đó trong các cuộc gọi tiếp theo để đặt Chế độ xem phù hợp. Điều này ghi đè Mã Autolayout theo nghĩa đó, nó diễn ra sau [super layoutSubviews]; có chứa mã tự động thanh toán.

Giống như vậy, không còn cần phải tránh Tự động xóa, nhưng bạn có thể tạo tự động thanh toán khi Hành vi mặc định không còn phù hợp. Tất nhiên bạn có thể áp dụng cách thức những thứ phức tạp hơn so với những gì trong Ví dụ đó nhưng đây là tất cả những gì tôi cần vì Ứng dụng của tôi chỉ có thể sử dụng Chế độ chân dung.


5
Cảm ơn vì đã đăng tải điều này. Kể từ khi tôi viết câu trả lời của mình, tôi chắc chắn đã hiểu được giá trị của việc ghi đè layoutSubviews. Tuy nhiên, tôi chỉ muốn nói thêm rằng Apple ở đây chỉ đơn thuần là bắt bạn làm những gì tôi nghĩ họ nên làm (trong quá trình thực hiện Autolayout).
matt

Bạn nói đúng nhưng điều này tôi đoán là không thực sự liên quan đến vấn đề. Vì họ đã cung cấp Mã trên, tôi rất vui vì điều đó!
Sensslen

4
Thật đáng buồn và vô lý khi chính nhóm tự động thanh toán đề xuất tạo một lớp con container tùy chỉnh chỉ để giải quyết cách thức tự động phá vỡ hành vi lâu dài và trực quan trong hệ thống xem.
tảo

8

Tôi tìm thấy một cách đơn giản. Và nó hoạt động trên iOS 8 và iOS 9.

Giống như điều chỉnh anchorPoint khi bạn sử dụng bố cục dựa trên khung:

let oldFrame = layerView.frame
layerView.layer.anchorPoint = newAnchorPoint
layerView.frame = oldFrame

Khi bạn điều chỉnh neo của chế độ xem với bố cục tự động, bạn sẽ làm điều tương tự nhưng theo cách hạn chế. Khi anchorPoint thay đổi từ (0,5, 0,5) thành (1, 0,5), layerView sẽ di chuyển sang trái với khoảng cách bằng một nửa chiều dài của chiều rộng khung nhìn, do đó bạn cần phải bù cho điều này.

Tôi không hiểu ràng buộc trong câu hỏi. Vì vậy, giả sử rằng bạn thêm một ràng buộc centreX liên quan đến superView centre với hằng số: layerView.centerX = superView.centerX + hằng số

layerView.layer.anchorPoint = CGPoint(1, 0.5)
let centerXConstraint = .....
centerXConstraint.constant = centerXConstraint.constant + layerView.bounds.size.width/2

Ly bia tốt nhất cho bạn của tôi. Đây là cách giải quyết rất tốt: D
Błażej

3

Nếu bạn đang sử dụng bố cục tự động, thì tôi sẽ không thấy cách cài đặt vị trí thủ công sẽ phục vụ lâu dài vì cuối cùng bố cục tự động sẽ ghi đè giá trị vị trí bạn đã đặt khi tính toán bố cục của chính nó.

Thay vào đó, điều cần thiết là tự sửa đổi các ràng buộc bố cục để bù đắp cho những thay đổi được tạo ra bằng cách đặt anchorPoint. Các chức năng sau đây làm điều đó cho các khung nhìn chưa được truyền.

/**
  Set the anchorPoint of view without changing is perceived position.

 @param view view whose anchorPoint we will mutate
 @param anchorPoint new anchorPoint of the view in unit coords (e.g., {0.5,1.0})
 @param xConstraint an NSLayoutConstraint whose constant property adjust's view x.center
 @param yConstraint an NSLayoutConstraint whose constant property adjust's view y.center

  As multiple constraints can contribute to determining a view's center, the user of this
 function must specify which constraint they want modified in order to compensate for the
 modification in anchorPoint
 */
void SetViewAnchorPointMotionlesslyUpdatingConstraints(UIView * view,CGPoint anchorPoint,
                                                       NSLayoutConstraint * xConstraint,
                                                       NSLayoutConstraint * yConstraint)
{
  // assert: old and new anchorPoint are in view's unit coords
  CGPoint const oldAnchorPoint = view.layer.anchorPoint;
  CGPoint const newAnchorPoint = anchorPoint;

  // Calculate anchorPoints in view's absolute coords
  CGPoint const oldPoint = CGPointMake(view.bounds.size.width * oldAnchorPoint.x,
                                 view.bounds.size.height * oldAnchorPoint.y);
  CGPoint const newPoint = CGPointMake(view.bounds.size.width * newAnchorPoint.x,
                                 view.bounds.size.height * newAnchorPoint.y);

  // Calculate the delta between the anchorPoints
  CGPoint const delta = CGPointMake(newPoint.x-oldPoint.x, newPoint.y-oldPoint.y);

  // get the x & y constraints constants which were contributing to the current
  // view's position, and whose constant properties we will tweak to adjust its position
  CGFloat const oldXConstraintConstant = xConstraint.constant;
  CGFloat const oldYConstraintConstant = yConstraint.constant;

  // calculate new values for the x & y constraints, from the delta in anchorPoint
  // when autolayout recalculates the layout from the modified constraints,
  // it will set a new view.center that compensates for the affect of the anchorPoint
  CGFloat const newXConstraintConstant = oldXConstraintConstant + delta.x;
  CGFloat const newYConstraintConstant = oldYConstraintConstant + delta.y;

  view.layer.anchorPoint = newAnchorPoint;
  xConstraint.constant = newXConstraintConstant;
  yConstraint.constant = newYConstraintConstant;
  [view setNeedsLayout];
}

Tôi thừa nhận đây có lẽ không phải là tất cả những gì bạn đã hy vọng, vì thông thường lý do duy nhất bạn muốn sửa đổi anchorPoint là để đặt một biến đổi. Điều đó sẽ yêu cầu một hàm phức tạp hơn, cập nhật các ràng buộc bố cục để phản ánh tất cả các thay đổi khung có thể do chính thuộc tính biến đổi gây ra. Điều này là khó khăn vì biến đổi có thể làm rất nhiều cho khung hình. Biến đổi tỷ lệ hoặc xoay sẽ làm cho khung lớn hơn, vì vậy chúng tôi cần cập nhật mọi ràng buộc về chiều rộng hoặc chiều cao, v.v.

Nếu bạn chỉ sử dụng biến đổi cho hoạt hình tạm thời, thì những gì ở trên có thể đủ vì tôi không tin bố cục tự động sẽ ngăn hoạt hình trên máy bay hiển thị hình ảnh thể hiện sự vi phạm hoàn toàn tạm thời các ràng buộc.


Tôi đang sử dụng biến đổi không may. Đặt vị trí là tốt vì phương thức được gọi mỗi khi chế độ xem được đặt, do đó, nó không bị ghi đè. Tôi đang nghĩ đến việc ghi đè lên layoutRect có thể là một giải pháp tốt hơn (được đề cập trong một trong những câu trả lời trước đó của bạn) nhưng tôi chưa thử.
jrturton

Nhân tiện, đây là một giàn thử tôi đã sử dụng. Sẽ thật tuyệt nếu ai đó phát hiện ra điều này: github.com/algal/AutolayoutAnchorPointTestRig
tảo

Đây phải là câu trả lời được chấp nhận. Câu trả lời của @ matt thậm chí không phải về điểm neo.
Iulian Onofrei

3

tl: dr: Bạn có thể tạo một ổ cắm cho một trong những ràng buộc để nó có thể được gỡ bỏ và thêm lại.


Tôi đã tạo một dự án mới và thêm một khung nhìn với kích thước cố định ở trung tâm. Các ràng buộc được hiển thị trong hình dưới đây.

Các ràng buộc cho ví dụ nhỏ nhất mà tôi có thể nghĩ ra.

Tiếp theo tôi đã thêm một lối thoát cho chế độ xem sẽ xoay và cho ràng buộc căn giữa x.

@property (weak, nonatomic) IBOutlet UIView *rotatingView;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *xAlignmentContstraint;

Sau này viewDidAppeartôi tính điểm neo mới

UIView *view = self.rotatingView;

CGPoint rotationPoint = // The point I'm rotating around... (only X differs)
CGPoint anchorPoint = CGPointMake((rotationPoint.x-CGRectGetMinX(view.frame))/CGRectGetWidth(view.frame),
                                  (rotationPoint.y-CGRectGetMinY(view.frame))/CGRectGetHeight(view.bounds));

CGFloat xCenterDifference = rotationPoint.x-CGRectGetMidX(view.frame);

view.layer.anchorPoint = anchorPoint;

Sau đó, tôi loại bỏ các ràng buộc mà tôi có một ổ cắm, tạo một ràng buộc mới được bù và thêm lại một lần nữa. Sau đó tôi nói với khung nhìn với các ràng buộc đã thay đổi rằng nó cần cập nhật các ràng buộc.

[self.view removeConstraint:self.xAlignmentContstraint];
self.xAlignmentContstraint = [NSLayoutConstraint constraintWithItem:self.rotatingView
                                                          attribute:NSLayoutAttributeCenterX
                                                          relatedBy:NSLayoutRelationEqual
                                                             toItem:self.view
                                                          attribute:NSLayoutAttributeCenterX
                                                         multiplier:1.0
                                                           constant:xDiff];
[self.view addConstraint:self.xAlignmentContstraint];
[self.view needsUpdateConstraints];

Cuối cùng tôi chỉ cần thêm hình ảnh động xoay vào chế độ xem xoay.

CABasicAnimation *rotate = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
rotate.toValue = @(-M_PI_2);
rotate.autoreverses = YES;
rotate.repeatCount = INFINITY;
rotate.duration = 1.0;
rotate.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; 

[view.layer addAnimation:rotate forKey:@"myRotationAnimation"];

Lớp xoay trông giống như được đặt ở giữa (mà nó nên) ngay cả khi xoay thiết bị hoặc làm cho nó cập nhật các ràng buộc. Các ràng buộc mới và điểm neo thay đổi trực quan triệt tiêu lẫn nhau.


1
Điều này sẽ làm việc cho tình huống cụ thể của tôi, nó không giống như một giải pháp chung tốt đẹp là có thể.
jrturton

1

Giải pháp hiện tại của tôi là tự điều chỉnh vị trí của lớp viewDidLayoutSubviews. Mã này cũng có thể được sử dụng layoutSubviewscho một lớp con khung nhìn, nhưng trong trường hợp của tôi, khung nhìn của tôi là một khung nhìn cấp cao nhất bên trong một bộ điều khiển khung nhìn, vì vậy điều này có nghĩa là tôi không phải tạo một lớp con UIView.

Có vẻ như quá nhiều nỗ lực để các câu trả lời khác được chào đón nhất.

-(void)viewDidLayoutSubviews
{
    for (UIView *view in self.view.subviews)
    {
        CGPoint anchorPoint = view.layer.anchorPoint;
        // We're only interested in views with a non-standard anchor point
        if (!CGPointEqualToPoint(CGPointMake(0.5, 0.5),anchorPoint))
        {
            CGFloat xDifference = anchorPoint.x - 0.5;
            CGFloat yDifference = anchorPoint.y - 0.5;
            CGPoint currentPosition = view.layer.position;

            // Use transforms if we can, otherwise manually calculate the frame change
            // Assuming a transform is in use since we are changing the anchor point. 
            if (CATransform3DIsAffine(view.layer.transform))
            {
                CGAffineTransform current = CATransform3DGetAffineTransform(view.layer.transform);
                CGAffineTransform invert = CGAffineTransformInvert(current);
                currentPosition = CGPointApplyAffineTransform(currentPosition, invert);
                currentPosition.x += (view.bounds.size.width * xDifference);
                currentPosition.y += (view.bounds.size.height * yDifference);
                currentPosition = CGPointApplyAffineTransform(currentPosition, current);
            }
            else
            {
                CGFloat transformXRatio = view.bounds.size.width / view.frame.size.width;

                if (xDifference < 0)
                    transformXRatio = 1.0/transformXRatio;

                CGFloat transformYRatio = view.bounds.size.height / view.frame.size.height;
                if (yDifference < 0)
                    transformYRatio = 1.0/transformYRatio;

                currentPosition.x += (view.bounds.size.width * xDifference) * transformXRatio;
                currentPosition.y += (view.bounds.size.height * yDifference) * transformYRatio;
            }
            view.layer.position = currentPosition;
        }

    }
}

1

Lấy cảm hứng từ câu trả lời của matt, tôi quyết định thử một cách tiếp cận khác. Một khung nhìn container, với các ràng buộc được áp dụng phù hợp, có thể được sử dụng. Chế độ xem với điểm neo được sửa đổi sau đó có thể được đặt trong chế độ xem vùng chứa, sử dụng mặt nạ tự động hóa và cài đặt khung rõ ràng giống như trong những ngày cũ tồi tệ.

Nó hoạt động một điều trị, cho tình huống của tôi nào. Các khung nhìn được thiết lập ở đây trong viewDidLoad:

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    UIView *redView = [UIView new];
    redView.translatesAutoresizingMaskIntoConstraints = NO;
    redView.backgroundColor = [UIColor redColor];
    [self.view addSubview:redView];

    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"|-[redView]-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(redView)]];
    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[redView]-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(redView)]];
    self.redView = redView;

    UIView *greenView = [UIView new];
    greenView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
    greenView.layer.anchorPoint = CGPointMake(1.0, 0.5);
    greenView.frame = redView.bounds;
    greenView.backgroundColor = [UIColor greenColor];
    [redView addSubview:greenView];
    self.greenView = greenView;

    CATransform3D perspective = CATransform3DIdentity;
    perspective.m34 = 0.005;
    self.redView.layer.sublayerTransform = perspective;
}

Tại thời điểm này, các khung hình cho chế độ xem màu đỏ không bằng 0, vì mặt nạ tự động hóa trên chế độ xem màu xanh lá cây.

Tôi đã thêm một biến đổi xoay trên một phương thức hành động và đây là kết quả:

nhập mô tả hình ảnh ở đây

Nó dường như tự mất đi trong quá trình quay thiết bị, vì vậy tôi đã thêm nó vào phương thức viewDidLayoutSubviews:

-(void)viewDidLayoutSubviews
{
    [super viewDidLayoutSubviews];
    [CATransaction begin];
    [CATransaction setDisableActions:YES];
    CATransform3D transform = self.greenView.layer.transform;
    self.greenView.layer.transform = CATransform3DIdentity;
    self.greenView.frame = self.redView.bounds;
    self.greenView.layer.transform = transform;
    [CATransaction commit];

}

Tôi tự hỏi liệu trong trường hợp của bạn, việc để view.layermột mình và làm tất cả công việc của bạn trong một lớp con sẽ không dễ dàng hơn view.layer. Nói cách khác, khung nhìn sẽ chỉ là một máy chủ lưu trữ, và tất cả các biến đổi của bản vẽ và lớp con, v.v. sẽ là một mức giảm, không bị ảnh hưởng bởi các ràng buộc.
matt

1
Được rồi, lấy cảm hứng từ giải pháp xem xét của bạn, tôi đã thêm nó vào bài luận của mình! Cảm ơn vì đã cho tôi chơi trong hộp cát của bạn ...
matt

0

Tôi nghĩ rằng bạn đang đánh bại mục đích tự động thanh toán bằng phương pháp đó. Bạn đã đề cập rằng chiều rộng và cạnh phải phụ thuộc vào giám sát, vậy tại sao không thêm các ràng buộc dọc theo dòng suy nghĩ đó?

Mất mô hình neo / biến đổi và thử:

[self.view addConstraint:
[NSLayoutConstraint constraintWithItem:layerView
                             attribute:NSLayoutAttributeRight
                             relatedBy:NSLayoutRelationEqual 
                                toItem:self.view 
                             attribute:NSLayoutAttributeWidth 
                            multiplier:1.0f
                              constant:-somePadding]];
[self.view addConstraint:
[NSLayoutConstraint constraintWithItem:layerView
                             attribute:NSLayoutAttributeWidth
                             relatedBy:NSLayoutRelationEqual 
                                toItem:someViewWeDependTheWidthOn
                             attribute:NSLayoutAttributeWidth 
                            multiplier:0.5f // because you want it to be half of someViewWeDependTheWidthOn
                              constant:-20.0f]]; // your 20pt offset from the left

Các NSLayoutAttributeRightphương tiện hạn chế giống hệt anchorPoint = CGPointMake(1.0, 0.5), và NSLayoutAttributeWidthhạn chế là tương đương với mã trước đó của bạn NSLayoutAttributeLeft.


Cảm ơn câu trả lời của bạn, nhưng tôi không thể "mất điểm neo / mô hình biến đổi". Tôi cần áp dụng một biến đổi, và điều chỉnh điểm neo để thực hiện biến đổi chính xác.
jrturton

Để giải thích thêm, trong trường hợp cụ thể này, chế độ xem được chuyển đổi đôi khi sẽ cần phải gập vào màn hình bằng cách xoay dọc theo trục Y trên cạnh phải của nó. Do đó, điểm neo phải được di chuyển. Tôi cũng đang tìm kiếm một giải pháp chung.
jrturton

Tất nhiên bạn vẫn có thể giữ anchorPoint, quan điểm của tôi là bạn không nên sử dụng nó để đo lường. Hệ thống tự động thanh toán UIView phải độc lập với các biến đổi CALayer. Vì vậy, UIView: layout, CALayer: xuất hiện / hoạt hình
John Estropia

1
Nó nên như vậy, nhưng nó không phải là. Bạn đã thực sự thử điều này? Thay đổi điểm neo bù đắp vị trí của lớp sau khi bố trí tự động đã hoàn thành công việc của nó. Giải pháp của bạn không hoạt động với điểm neo được sửa đổi.
jrturton

Có, tôi có thể sử dụng các neo khác nhau cho các chế độ xem được đặt ra với các ràng buộc. Câu trả lời của bạn viewDidLayoutSubviewsnên khắc phục điều đó; positionluôn luôn đi cùng với anchorPoint. Câu trả lời của tôi chỉ cho thấy cách xác định ràng buộc cho biến đổi danh tính.
John Estropia

0

Câu hỏi và câu trả lời này đã truyền cảm hứng cho tôi để giải quyết các vấn đề của riêng tôi với Autolayout và chia tỷ lệ, nhưng với các cuộn xem. Tôi đã tạo một ví dụ về giải pháp của mình trên github:

https://github.com/hansdesmedt/AutoLayout-scrollview-scale

Đây là một ví dụ về UIScrollView với phân trang tùy chỉnh hoàn toàn được thực hiện trong AutoLayout và có thể mở rộng (CATransform3DMakeScale) bằng cách nhấn và nhấn để phóng to. Tương thích iOS 6 và 7.


0

Đây là một chủ đề lớn và tôi chưa đọc tất cả các ý kiến ​​nhưng đang đối mặt với cùng một vấn đề.

Tôi đã có một cái nhìn từ XIB với autolayout. Và tôi muốn cập nhật tài sản biến đổi của nó. Việc đưa chế độ xem vào chế độ xem vùng chứa không giải quyết được vấn đề của tôi vì tính năng tự động thanh toán đã hoạt động kỳ lạ trên chế độ xem vùng chứa. Đó là lý do tại sao tôi chỉ thêm chế độ xem vùng chứa thứ hai để chứa chế độ xem vùng chứa chứa chế độ xem của tôi và đang áp dụng các biến đổi trên nó.


0

tl; dr Giả sử bạn đã thay đổi điểm neo thành (0, 0). Điểm neo bây giờ là trên cùng bên trái. Bất cứ khi nào bạn thấy trung tâm từ trong bố trí tự động, bạn nên nghĩ trên cùng bên trái .

Khi bạn điều chỉnh anchorPoint, bạn chỉ cần thay đổi ngữ nghĩa của AutoLayout. Bố cục tự động sẽ không can thiệp vào anchorPoint của bạn cũng như ngược lại. Nếu bạn không hiểu điều này, bạn sẽ có một khoảng thời gian tồi tệ .


Thí dụ:

Hình A. Không sửa đổi điểm neo

#Before changing anchor point to top-left
view.size == superview.size
view.center == superview.center

Hình B. Điểm neo thay đổi sang trên cùng bên trái

view.layer.anchorPoint = CGPointMake(0, 0)
view.size == superview.size
view.center == superview.topLeft                <----- L0-0K, center is now top-left

Hình A và Hình B trông giống hệt nhau. Không có gì thay đổi. Chỉ cần định nghĩa của những gì trung tâm đề cập đến thay đổi.


Đó là tất cả tốt và tốt nhưng không trả lời câu hỏi phải làm gì khi các ràng buộc của bạn không liên quan đến trung tâm.
jrturton
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.