자동 레이아웃을 사용하는 경우 CALayer의 기준점을 어떻게 조정합니까?


161

참고 :이 질문이 제기 된 이후 상황이 진행되었습니다. 최근의 개요를 보려면 여기 를 참조 하십시오 .


자동 레이아웃 전에 프레임을 저장하고 앵커 포인트를 설정하고 프레임을 복원하여 뷰를 이동하지 않고 뷰 레이어의 앵커 포인트를 변경할 수 있습니다.

자동 레이아웃 세계에서는 더 이상 프레임을 설정하지 않지만 뷰의 위치를 ​​원하는 위치로 다시 조정하는 작업에는 제약 조건이 적용되지 않습니다. 제약 조건을 해킹하여 뷰의 위치를 ​​조정할 수 있지만 회전 또는 다른 크기 조정 이벤트에서는 다시 무효화됩니다.

다음과 같은 밝은 아이디어는 "레이아웃 속성의 잘못된 쌍 (왼쪽 및 너비)"을 생성하므로 작동하지 않습니다.

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

내 의도 layerView는 조정 된 앵커 포인트가있는 뷰 의 왼쪽 가장자리를 너비의 절반에 20을 더한 값 (수퍼 뷰의 왼쪽 가장자리에서 삽입하려는 거리)으로 설정하는 것입니다.

자동 배치로 배치 된 뷰에서 뷰의 위치를 ​​변경하지 않고 앵커 포인트를 변경할 수 있습니까? 하드 코딩 된 값을 사용하고 모든 회전에 대한 구속 조건을 편집해야합니까? 나는 희망하지 않습니다.

뷰에 변형을 적용 할 때 올바른 시각 효과를 얻을 수 있도록 앵커 포인트를 변경해야합니다.


여기에있는 것을 감안할 때 위의 코드가 작동하더라도 어쨌든 모호한 레이아웃으로 끝날 것 같습니다. layerView너비를 어떻게 알 수 있습니까? 오른쪽이 다른 것을 고집하고 있습니까?
존 에스트로 피아

즉,은으로 덮여 //Size-related constraints that work fine- 층 뷰의 폭과 높이가 수퍼의으로부터 유도된다.
jrturton

답변:


361

[편집 : 경고 : 후속 토론 전체가 iOS 8에 의해 낡았거나 적어도 크게 완화되어 뷰 변환이 적용될 때 레이아웃을 트리거하는 실수를 더 이상하지 않을 수 있습니다.]

자동 레이아웃 vs. 뷰 변환

자동 레이아웃은 뷰 변환에서 전혀 잘 재생되지 않습니다. 내가 알 수있는 한 그 이유는 변환 (기본 ID 변환 이외)이있는 뷰의 프레임을 엉망으로 만들지 않아야하기 때문입니다. 그러나 이것이 자동 레이아웃이하는 것입니다. 자동 레이아웃이 작동하는 방식 layoutSubviews은 런타임에서 모든 제약 조건을 따라 파싱하고 모든 뷰의 프레임을 적절하게 설정하는 것입니다.

다시 말해, 제약은 마술이 아닙니다. 그들은 단지 할 일 목록 일뿐입니다.layoutSubviews할 일 목록이 완성되는 곳입니다. 그리고 프레임을 설정하여 수행합니다.

나는 이것을 버그로 도울 수 없다. 이 변환을 뷰에 적용하면 :

v.transform = CGAffineTransformMakeScale(0.5,0.5);

뷰가 중심과 함께 이전 크기와 절반 크기로 나타납니다. 그러나 제약 조건에 따라 내가 전혀 보지 못할 수도 있습니다.

[실제로 두 번째 놀라운 점이 있습니다. 뷰에 변환을 적용하면 레이아웃이 즉시 트리거됩니다. 이것은 나에게 또 다른 버그 인 것 같습니다. 아니면 아마도 첫 번째 버그의 핵심 일 것입니다. 내가 기대할 수있는 것은 레이아웃 시간까지 최소한 프레임 시간으로 프레임 애니메이션으로 도망 갈 수있는 것처럼 레이아웃 시간까지 장치를 회전시킬 때까지 변환으로 벗어날 수 있다는 것입니다. 그러나 실제로 레이아웃 시간은 즉각적이며 이는 잘못된 것 같습니다.]

해결 방법 1 : 제한 없음

현재 해결책 중 하나는 반영구적 변환을 뷰에 적용하고 (일시적으로 흔들리는 것이 아니라) 뷰에 영향을 미치는 모든 제약 조건을 제거하는 것입니다. 불행히도 자동 레이아웃이 여전히 발생하기 때문에 일반적으로 화면에서 뷰가 사라지고 뷰를 배치 할 위치를 알려주는 제약이 없습니다. 따라서 제약 조건을 제거하는 것 외에도보기 translatesAutoresizingMaskIntoConstraints를 YES로 설정했습니다 . 뷰는 이제 자동 레이아웃의 영향을받지 않고 이전 방식으로 작동합니다. (그것은 됩니다 분명, 자동 레이아웃에 영향을하지만, 암시 적 자동 크기 마스크 제약의 동작이 자동 레이아웃 전과 마찬가지로 원인이 될.)

해결 방법 2 : 적절한 제약 조건 만 사용

약간 과감한 것처럼 보이면 다른 솔루션은 의도 한 변환에서 제약 조건이 올바르게 작동하도록 설정하는 것입니다. 예를 들어 뷰의 내부 고정 너비와 높이에 따라 크기가 정해지고 중심에 의해 순수하게 배치되면 스케일 변환이 예상대로 작동합니다. 이 코드에서는 하위 뷰 ( otherView) 의 기존 제약 조건을 제거하고 해당 제약 조건을 네 가지 제약 조건으로 대체하여 너비와 높이를 고정하고 중심을 기준으로 고정합니다. 그 후 스케일 변환이 작동합니다.

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

결론은 뷰의 프레임에 영향을 미치는 구속 조건이없는 경우 자동 레이아웃은 뷰의 프레임을 건드리지 않는다는 것입니다.

해결 방법 3 : 하위 뷰 사용

위의 두 가지 해결 방법의 문제점은 제약 조건의 이점을 잃어 시야를 확보 할 수 없다는 것입니다. 이를 해결하는 솔루션이 있습니다. 작업이 호스트 역할만을하는 보이지 않는보기로 시작하고 제약 조건을 사용하여 배치하십시오. 그 안에 실제 뷰를 하위 뷰로 넣습니다. 제약 조건을 사용하여 호스트 뷰 내에 하위 뷰를 배치 할 수 있지만 변환을 적용 할 때 반박하지 않는 제약 조건으로 제한 조건을 제한하십시오.

그림은 다음과 같습니다.

여기에 이미지 설명을 입력하십시오

흰색보기는 호스트보기입니다. 투명하고 보이지 않는 것처럼 가장해야합니다. 빨간색 뷰는 중앙을 호스트 뷰의 중앙에 고정하여 배치 된 하위 뷰입니다. 이제 우리는 아무런 문제없이 중앙을 중심으로 빨간색보기를 확장하고 회전시킬 수 있으며 실제로 그림은 우리가 그렇게했음을 보여줍니다.

self.otherView.transform = CGAffineTransformScale(self.otherView.transform, 0.5, 0.5);
self.otherView.transform = CGAffineTransformRotate(self.otherView.transform, M_PI/8.0);

한편 호스트보기의 제약 조건은 장치를 회전 할 때 올바른 위치에 유지됩니다.

해결 방법 4 : 대신 레이어 변환 사용

뷰 변환 대신 레이아웃을 트리거하지 않으므로 구속 조건과 즉시 충돌하지 않는 레이어 변환을 사용하십시오.

예를 들어,이 간단한 "throb"뷰 애니메이션은 자동 레이아웃에서 잘릴 수 있습니다.

[UIView animateWithDuration:0.3 delay:0
                    options:UIViewAnimationOptionAutoreverse
                 animations:^{
    v.transform = CGAffineTransformMakeScale(1.1, 1.1);
} completion:^(BOOL finished) {
    v.transform = CGAffineTransformIdentity;
}];

결국 뷰의 크기에는 변화가 없었지만 transform레이아웃을 설정 하면 레이아웃이 발생하고 제약 조건으로 인해 뷰가 건너 뛸 수 있습니다. (버그처럼 보입니까?) 그러나 CABasicAnimation을 사용하고 뷰 레이어에 애니메이션을 적용하는 Core Animation과 동일한 작업을 수행하면 레이아웃이 발생하지 않으며 제대로 작동합니다.

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
변환은 문제의 원인이 아니라 새로운 앵커 포인트를 설정하는 것입니다. 제약 조건을 제거하고 자동 크기 조정 마스크를 시도해 보겠습니다.
jrturton

1
나는 여기서 뭔가를 이해하지 못한다. 해결 방법 4 : 변환이 자동 레이아웃에도 영향을주는 백업 레이어에 변형을 적용 할 때 뷰의 변형 속성이 백업 레이어의 변형을 참조하기 때문에
Luca Bartoletti

1
@Andy 반대로, iOS 8에서는 문제가 완전히 사라졌고 전체 답변이 필요하지 않습니다.
Matt

1
@ Sunkas 귀하의 문제에 답변했습니다.
Ben Sinclair

2
iOS 8에서 레이어 변환도 자동 레이아웃을 트리거합니다
CarmeloS

41

나는 비슷한 Isuue를 가지고 있었고 Apple의 Autolayout Team으로부터 Back을 들었습니다. 그들은 Container View Approach를 사용하는 것이 좋습니다 매트는 제안하지만 layoutSubviews를 덮어 쓰고 사용자 정의 레이아웃 코드를 적용하기 위해 UIView의 하위 클래스를 만듭니다. 매력처럼 작동합니다.

헤더 파일은 Interface Builder에서 직접 선택한 하위보기를 링크 할 수있는 모양입니다.

#import <UIKit/UIKit.h>

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

m 파일은 다음과 같은 특수 코드를 적용합니다.

#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

보시다시피, 처음 호출 할 때 뷰의 중심점을 잡고 추가 호출에서 해당 위치를 재사용하여 뷰를 적절하게 배치합니다. 이는 자동 레이아웃 코드를 덮어 쓰고 [super layoutSubviews] 이후에 발생합니다. 자동 레이아웃 코드가 포함되어 있습니다.

이와 같이 더 이상 자동 레이아웃을 피할 필요는 없지만 기본 동작이 더 이상 적절하지 않은 경우 자체 자동 레이아웃을 만들 수 있습니다. 물론 당신은 그 예제에있는 것보다 더 복잡한 것들을 적용 할 수 있지만 이것은 내 앱이 세로 모드만을 사용할 수 있기 때문에 필요한 전부였습니다.


5
이것을 게시 해 주셔서 감사합니다. 답변을 썼으므로 재정의의 가치를 분명히 이해하게되었습니다 layoutSubviews. 하지만 여기 애플이 만드는 것만 추가하면됩니다 당신은 내가 무슨 생각을 할 그들이 모두 함께 (자동 레이아웃의 구현에) 수행해야합니다.
matt

당신 말이 맞지만 이것은 실제로 문제 자체와 관련이 없다고 생각합니다. 그들이 위의 코드를 제공했기 때문에 나는 그것을 기쁘게 생각합니다!
sensslen

4
자동 레이아웃 팀은 자동 레이아웃이 뷰 시스템에서 오래 지속되고 직관적 인 동작을 중단시키는 방식을 해결하기 위해 사용자 지정 컨테이너 하위 클래스를 만드는 것이 좋습니다.
algal

8

나는 간단한 방법을 찾습니다. 그리고 그것은 iOS 8과 iOS 9에서 작동합니다.

프레임 기반 레이아웃을 사용할 때 anchorPoint 조정과 같이 :

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

자동 레이아웃을 사용하여 뷰의 앵커를 조정하면 동일한 방식으로 제한을받지 않습니다. anchorPoint가 (0.5, 0.5)에서 (1, 0.5)로 변경되면 layerView는 뷰 너비의 절반 길이만큼 왼쪽으로 이동하므로이를 보정해야합니다.

질문의 제약 조건을 이해하지 못하므로 상수를 사용하여 superView centerX에 상대적인 centerX 제약 조건을 추가한다고 가정하십시오. layerView.centerX = superView.centerX + constant

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

내 친구에게 최고의 맥주 한잔. 이것은 훌륭한 해결책입니다 : D
Błażej

3

자동 레이아웃을 사용하는 경우 자동 레이아웃이 자체 레이아웃을 계산할 때 설정 한 위치 값을 파악하기 때문에 수동으로 위치를 수동으로 설정하는 것이 장기적으로 어떻게 작동하는지 알 수 없습니다.

오히려 필요한 것은 anchorPoint를 설정하여 생성 된 변경 사항을 보상하기 위해 레이아웃 제약 조건 자체를 수정하는 것입니다. 다음 함수는 변환되지 않은 뷰에 대해 수행합니다.

/**
  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];
}

일반적으로 anchorPoint를 수정하려는 유일한 이유는 변환을 설정하는 것이기 때문에 이것이 당신이 바라는 전부는 아니라는 것을 인정합니다. 변환 속성 자체로 인해 발생할 수있는 모든 프레임 변경 사항 을 반영하도록 레이아웃 제약 조건을 업데이트하는 더 복잡한 함수가 필요 합니다. 변환이 프레임에 많은 역할을 할 수 있기 때문에 까다 롭습니다. 스케일링 또는 회전 변환은 프레임을 더 크게 만들므로 너비 또는 높이 제한 등을 업데이트해야합니다.

임시 애니메이션에만 변환을 사용하는 경우 자동 레이아웃으로 인해 기내 애니메이션이 제약 조건을 일시적으로 위반하는 이미지를 표시하지 못할 것이라고 믿지 않기 때문에 위의 내용으로 충분할 수 있습니다.


불행히도 변환을 사용하고 있습니다. 뷰가 배치 될 때마다 메소드가 호출되므로 위치를 설정하는 것이 좋습니다. 따라서 방해받지 않습니다. layoutRect를 재정의하는 것이 더 나은 솔루션 일 수 있다고 생각하지만 (이전 답변 중 하나에서 언급) 아직 시도하지 않았습니다.
jrturton

그건 그렇고, 내가 사용하고있는 테스트 장비가 있습니다. 누군가 이것을 알아 내면 좋을
algal

이것이 정답입니다. @ matt의 대답은 앵커 포인트조차도 아닙니다.
Iulian Onofrei

3

tl : dr : 구속 조건 중 하나에 대한 콘센트를 작성하여 제거하고 다시 추가 할 수 있습니다.


새 프로젝트를 만들고 중앙에 고정 크기의 뷰를 추가했습니다. 제약 조건은 아래 이미지에 나와 있습니다.

내가 생각할 수있는 가장 작은 예에 대한 제약.

다음으로 회전 할 뷰와 중심 x 정렬 구속 조건에 대한 콘센트를 추가했습니다.

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

나중에 viewDidAppear새로운 앵커 포인트를 계산합니다

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;

그런 다음 콘센트가있는 제약 조건을 제거하고 오프셋 된 새 제약 조건을 만든 다음 다시 추가하십시오. 그런 다음 변경된 제약 조건이있는 뷰에 제약 조건을 업데이트해야한다고 알립니다.

[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];

마지막으로 회전 애니메이션을 회전 뷰에 추가합니다.

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"];

회전 레이어는 장치를 회전하거나 제약 조건을 업데이트하도록 할 때에도 중심에 머물러있는 것처럼 보입니다. 새로운 구속 조건과 변경된 앵커 포인트가 서로 시각적으로 상쇄됩니다.


1
이것은 내 특정 상황에서 효과가 있으며, 일반적인 해결책이 가능한 것처럼 보이지 않습니다.
jrturton

1

현재 해결 방법은에서 레이어의 위치를 ​​수동으로 조정하는 것입니다 viewDidLayoutSubviews. 이 코드 layoutSubviews는 뷰 서브 클래스 에도 사용될 수 있지만, 제 뷰는 뷰 컨트롤러 내부의 최상위 뷰이므로 UIView 서브 클래스를 만들 필요가 없습니다.

너무 많은 노력으로 보이므로 다른 답변을 가장 환영합니다.

-(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

매트의 대답에 영감을 주면서 다른 접근법을 시도하기로 결정했습니다. 구속 조건을 적절히 적용한 컨테이너 뷰를 사용할 수 있습니다. 그런 다음 수정 된 앵커 포인트가있는 뷰는 자동 크기 조정 마스크와 나쁜 예전처럼 명시 적 프레임 설정을 사용하여 컨테이너 뷰 내에 배치 할 수 있습니다.

어쨌든 내 상황에 맞는 대우를합니다. 뷰는 여기 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;
}

녹색 시점의 자동 크기 조정 마스크로 인해이 시점에서 적색 시점의 프레임이 0이되는 것은 중요하지 않습니다.

액션 메소드에 회전 변환을 추가했는데 그 결과는 다음과 같습니다.

여기에 이미지 설명을 입력하십시오

장치 회전 중에 스스로 손실되는 것처럼 보였으므로 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];

}

귀하의 경우에 view.layer혼자 떠나서 모든 작업을 하위 계층에서 수행 하는 것이 쉽지 않은지 궁금합니다 view.layer. 다시 말해, 뷰는 호스트 일 뿐이며 모든 도면 및 하위 레이어 변형 등은 구속 조건의 영향을받지 않고 레벨이 낮아집니다.
matt

1
자, 여러분의 서브 뷰 솔루션에서 영감을 얻은 결과를 내 에세이에 추가했습니다! 샌드 박스에서 놀아 주셔서 감사합니다 ...
matt

0

그 방법으로 자동 레이아웃의 목적을 잃고 있다고 생각합니다. 너비와 오른쪽 가장자리는 수퍼 뷰에 의존한다고 언급 했으므로 왜 그 사고 선을 따라 구속 조건을 추가하지 않겠습니까?

anchorPoint / transform 패러다임을 잃고 다음을 시도하십시오.

[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

NSLayoutAttributeRight정확히 같은 제약 수단 anchorPoint = CGPointMake(1.0, 0.5), 그리고 NSLayoutAttributeWidth제약은 이전 코드의 거의 비슷하다 NSLayoutAttributeLeft.


답변 해 주셔서 감사하지만 "고정 점을 잃어 버리거나 패러다임을 바꿀 수는 없습니다". 변환을 적용하고 앵커 포인트를 조정하여 올바른 변환을 수행해야합니다.
jrturton

더 구체적으로 설명하자면,이 특정한 경우에 변환 된 뷰는 때때로 맨 오른쪽 가장자리에서 Y 축을 따라 회전하여 화면으로 접어야합니다. 따라서 앵커 포인트를 이동해야합니다. 나는 또한 일반적인 해결책을 찾고 있습니다.
jrturton

물론 당신은 여전히을 유지할 수 있습니다 anchorPoint, 내 요점은 측정에 사용해서는 안됩니다. UIView 자동 레이아웃 시스템은 CALayer 변환과 독립적이어야합니다. UIView : layout, CALayer : appearance / animations
John Estropia

1
되어야하지만 그렇지 않습니다. 실제로 이것을 시도 했습니까? 기준점을 변경하면 자동 레이아웃이 작업을 완료 한 후 레이어의 위치가 오프셋 됩니다. 수정 된 앵커 포인트로 솔루션이 작동하지 않습니다.
jrturton

예, 제약 조건으로 배치 된 뷰에 다른 앵커 포인트를 사용할 수 있습니다. 당신의 대답은 viewDidLayoutSubviews그것을 고쳐야합니다. position항상 함께anchorPoint . 내 대답은 단지 아이덴티티 변환에 대한 제약 조건을 정의하는 방법을 보여줍니다.
John Estropia 8

0

이 질문과 답변은 자동 레이아웃 및 스케일링과 관련하여 스크롤 뷰와 관련된 문제를 해결하는 데 영감을주었습니다. github에서 내 솔루션의 예를 만들었습니다.

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

이것은 AutoLayout에서 완전히 사용자 정의 페이징을 사용하는 UIScrollView의 예이며 길게 눌러 확대 / 축소 할 수있는 확장 가능 (CATransform3DMakeScale)입니다. iOS 6 및 7과 호환됩니다.


0

그것은 큰 주제이며 모든 의견을 읽지는 않았지만 같은 문제에 직면했습니다.

자동 레이아웃이있는 XIB에서 볼 수있었습니다. 그리고 변환 속성을 업데이트하고 싶었습니다. 자동 레이아웃이 컨테이너 뷰에서 이상하게 작동했기 때문에 뷰를 컨테이너 뷰에 포함해도 문제가 해결되지 않습니다. 그렇기 때문에 두 번째 컨테이너보기를 추가하여 내보기가 포함 된 컨테이너보기를 포함하고 변형을 적용했습니다.


0

tl; dr 앵커 포인트를 (0, 0)으로 변경했다고 가정 해 봅시다. 기준점이 왼쪽 상단에 있습니다. 자동 레이아웃에서 단어 중심 을 볼 때마다 왼쪽 상단 을 생각해야합니다 .

anchorPoint를 조정할 때 AutoLayout의 의미를 변경하기 만하면됩니다. 자동 레이아웃은 anchorPoint를 방해하지 않으며 그 반대도 마찬가지입니다. 당신이 이것을 이해하지 못하면, 당신은 나쁜 시간을 가질 것 입니다.


예:

그림 A. 앵커 포인트 수정 없음

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

그림 B. 앵커 포인트가 왼쪽 상단으로 변경되었습니다 .

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

그림 A와 그림 B는 정확히 동일하게 보입니다. 아무것도 바뀌지 않았다. 중심에 대한 정의 만 변경되었습니다.


그것은 모두 좋고 훌륭하지만 구속 조건이 중심과 관련이 없을 때 무엇을해야하는지에 대한 질문에는 답하지 않습니다.
jrturton
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.