Как настроить точку привязки CALayer, когда используется Auto Layout?

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 (расстояние я хочу застегивается от левого края надтаблицы).

Можно ли изменить опорную точку, не меняя местоположения вида, в виде с автоматическим макетом? Нужно ли использовать жестко закодированные значения и редактировать ограничение для каждого поворота? Я надеюсь, что нет.

Мне нужно изменить опорную точку, чтобы при применении преобразования к представлению я получал правильный визуальный эффект.

jrturton
источник
Учитывая то, что у вас есть, кажется, что вы все равно получите неоднозначные макеты, даже если у вас есть код, работающий выше. Как layerViewузнать его ширину? Он придерживается своей правой стороны чему-то другому?
Джон Эстропия
Это рассматривается в //Size-related constraints that work fine- ширина и высота представления слоя получаются из суперпредставления.
Джртуртон

Ответы:

361

[РЕДАКТИРОВАТЬ: Предупреждение: все последующее обсуждение, возможно, будет устаревшим или, по крайней мере, сильно смягченным iOS 8, которая больше не может ошибиться, вызывая компоновку во время применения преобразования представления.]

Autolayout vs. View Transforms

Autolayout не очень хорошо работает с трансформациями вида. Причина, насколько я могу заметить, заключается в том, что вы не должны связываться с фреймом представления, имеющего преобразование (отличное от преобразования идентификаторов по умолчанию) - но это именно то, что делает autolayout. Способ работы autolayout заключается в том, что во layoutSubviewsвремя выполнения преодолеваются все ограничения и соответственно устанавливаются кадры всех представлений.

Другими словами, ограничения не волшебство; они просто список дел. layoutSubviewsгде список дел делается. И делает это, устанавливая рамки.

Я не могу не расценивать это как ошибку. Если я применю это преобразование к представлению:

v.transform = CGAffineTransformMakeScale(0.5,0.5);

Я ожидаю увидеть вид с центром в том же месте, что и раньше, и в половине размера. Но, в зависимости от его ограничений, это может быть совсем не то, что я вижу.

[На самом деле, здесь есть второй сюрприз: применение преобразования к представлению немедленно запускает компоновку. Это кажется мне еще одной ошибкой. Или, возможно, это сердце первой ошибки. То, что я ожидал бы, - это иметь возможность уйти с преобразованием по крайней мере до времени компоновки, например, устройство поворачивается - так же, как я могу сойти с анимацией кадра до времени компоновки. Но на самом деле время макета является немедленным, что кажется просто неправильным.]

Решение 1: нет ограничений

Одним из текущих решений является, если я собираюсь применить полупостоянное преобразование к представлению (а не просто как-то временно его покачивать), чтобы удалить все ограничения, влияющие на него. К сожалению, это, как правило, приводит к исчезновению вида с экрана, поскольку автоматическое расположение по-прежнему имеет место, и теперь нет никаких ограничений, указывающих нам, куда поместить представление. Поэтому, в дополнение к удалению ограничений, я установил в translatesAutoresizingMaskIntoConstraintsYES вид. Представление теперь работает по-старому, фактически не затрагиваемое автоматическим размещением. (Это будет влиять autolayout, очевидно, но неявная маска ограничение автоматического изменения вызывает его поведение , чтобы быть точно так же как это было раньше autolayout.)

Решение 2. Используйте только соответствующие ограничения

Если это кажется немного радикальным, другое решение состоит в том, чтобы установить ограничения для правильной работы с предполагаемым преобразованием. Например, если представление имеет размер только по его внутренней фиксированной ширине и высоте и расположено исключительно по его центру, преобразование масштаба будет работать так, как я ожидаю. В этом коде я удаляю существующие ограничения в subview ( 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: Используйте Subview

Проблема с обоими вышеупомянутыми решениями состоит в том, что мы теряем преимущества ограничений для позиционирования нашей точки зрения. Итак, вот решение, которое решает это. Начните с невидимого представления, работа которого состоит исключительно в том, чтобы выступать в роли хоста, и используйте ограничения для его позиционирования. Внутри этого, поместите реальный взгляд как подпредставление. Используйте ограничения, чтобы расположить подпредставление в представлении хоста, но ограничьте эти ограничения ограничениями, которые не будут сопротивляться, когда мы применяем преобразование.

Вот иллюстрация:

введите описание изображения здесь

Белый вид - это вид хоста; Вы должны притворяться, что оно прозрачное и, следовательно, невидимое. Красный вид - это его подпредставление, расположенное путем прикрепления его центра к центру основного вида. Теперь мы можем без проблем масштабировать и вращать красный вид вокруг его центра, и на самом деле иллюстрация показывает, что мы сделали это:

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

Между тем, ограничения на представление хоста сохраняют его в нужном месте, когда мы вращаем устройство.

Решение 4. Вместо этого используйте преобразования слоев

Вместо преобразований вида используйте преобразования слоя, которые не вызывают компоновку и, следовательно, не вызывают немедленного конфликта с ограничениями.

Например, эта простая «пульсирующая» анимация вида может полностью сломаться при автоматическом размещении:

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

Несмотря на то, что в конце не было никаких изменений в размере представления, просто установление его transformрасположения вызывает, и ограничения могут заставить представление прыгать вокруг. (Это похоже на ошибку или что?) Но если мы сделаем то же самое с Core Animation (используя CABasicAnimation и применив анимацию к слою представления), макет не произойдет, и он будет работать нормально:

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: Когда преобразование применяется к вспомогательному слою, которое также влияет на автоматическое расположение, потому что свойство преобразования представления относится к преобразованию вспомогательного слоя
Лука Бартолетти,
1
@ И наоборот, на iOS 8 проблема полностью исчезла, и весь мой ответ не нужен
Мэтт
1
@ Sunkas Я ответил в твоем выпуске.
Бен Синклер
2
На iOS 8 преобразование слоев также запускает автоматическое размещение
CarmeloS
41

У меня был похожий Isuue, и я только что услышал Back от команды Autolayout в Apple. Они предлагают использовать подход «Подход к представлению контейнера», но они создают подкласс UIView, чтобы перезаписывать layoutSubviews и применять там собственный код макета. Это работает как шарм.

Заголовочный файл выглядит так, что вы можете связать выбранное вами подпредставление напрямую из 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

Как вы можете видеть, он захватывает центральную точку просмотра при первом вызове и повторно использует эту позицию в последующих вызовах, чтобы разместить представление соответствующим образом. Это переписывает код Autolayout в том смысле, что это происходит после [super layoutSubviews]; который содержит код разметки.

Таким образом, больше нет необходимости избегать Autolayout, но вы можете создать свою собственную autolayout, когда поведение по умолчанию больше не подходит. Конечно, вы можете применять более сложные вещи, чем в этом примере, но это было все, что мне было нужно, так как мое приложение может использовать только портретный режим.

sensslen
источник
5
Спасибо за публикацию. С тех пор как я написал свой ответ, я определенно пришел к пониманию ценности переопределения layoutSubviews. Я бы просто добавил, что Apple здесь просто заставляет вас делать то, что, я думаю, они должны были делать (в своей реализации Autolayout) все время.
матовые
Вы правы, но это, я думаю, на самом деле не связано с самой проблемой. Так как они предоставили вышеуказанный кодекс, я доволен этим!
Sensslen
4
Грустно и абсурдно, что команда autolayout сама рекомендует создать собственный подкласс контейнера, чтобы обойти, как autolayout нарушает давнее и интуитивное поведение в системе представления.
Водоросль
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 переместится влево на расстояние, равное половине длины ширины вида, поэтому вам необходимо это компенсировать.

Я не понимаю ограничение в вопросе. Итак, предположим, что вы добавляете ограничение centerX относительно superView centerX с константой: layerView.centerX = superView.centerX + constant

layerView.layer.anchorPoint = CGPoint(1, 0.5)
let centerXConstraint = .....
centerXConstraint.constant = centerXConstraint.constant + layerView.bounds.size.width/2
seedante
источник
Бокал лучшего пива для тебя, мой друг. Это очень хороший обходной путь: 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, - это установить преобразование. Это потребовало бы более сложной функции, которая обновляет ограничения макета, чтобы отразить все изменения кадра, которые могут быть вызваны самим свойством transform. Это сложно, потому что преобразования могут многое сделать для кадра. Преобразование масштабирования или поворота сделает кадр больше, поэтому нам нужно обновить любые ограничения ширины или высоты и т. Д.

Если вы используете преобразование только для временной анимации, то вышеописанного может быть достаточно, поскольку я не верю, что автоматическая компоновка не позволит анимации в полете представить изображения, которые представляют собой чисто временные нарушения ограничений.

водорослевые
источник
Я использую преобразования, к сожалению. Устанавливать позицию хорошо, потому что метод вызывается каждый раз, когда выкладывается представление, поэтому оно не засоряется. Я думаю, что переопределение layoutRect может быть лучшим решением (упомянуто в одном из ваших предыдущих ответов), но я еще не пробовал.
Jrturton
Кстати, вот тестовая установка, которую я использовал. Было бы здорово, если бы кто-то понял это: github.com/algal/AutolayoutAnchorPointTestRig
algal
Это должен быть принятый ответ. @ ответ Мэтта даже не в точке крепления.
Юлиан
3

tl: dr: Вы можете создать выход для одного из ограничений, чтобы его можно было удалить и снова добавить.


Я создал новый проект и добавил вид с фиксированным размером в центре. Ограничения показаны на рисунке ниже.

Ограничения для наименьшего примера, который я мог придумать.

Затем я добавил выход для вида, который будет вращаться, и для ограничения выравнивания центра х.

@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;
        }

    }
}
jrturton
источник
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;
}

Неважно, что кадры для красного вида на этом этапе равны нулю из-за маски авторазмера на зеленом виде.

Я добавил преобразование вращения в метод действия, и это было результатом:

введите описание изображения здесь

Казалось, что он теряет себя во время вращения устройства, поэтому я добавил это в метод 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];

}
jrturton
источник
Интересно, не было бы проще в твоем случае просто оставить в view.layerпокое и делать всю свою работу в подслое view.layer. Другими словами, представление будет просто хостом, а все преобразования чертежа и подслоя и т. Д. Будут иметь уровень вниз, не подверженный влиянию ограничений.
Мэтт
1
Хорошо, вдохновленный вашим решением для подпредставления, я добавил это к своему эссе! Спасибо, что позволили мне поиграть в вашей песочнице ...
Мэтт
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.

Джон Эстропия
источник
Спасибо за ваш ответ, но я не могу "потерять опорную точку / парадигму преобразования". Мне нужно применить преобразование и настроить точку привязки, чтобы сделать правильное преобразование.
Джртуртон
Чтобы объяснить далее, в этом конкретном случае преобразованный вид иногда нужно будет складывать в экран, поворачивая вдоль оси Y на его крайнем правом краю. Следовательно, опорная точка должна быть перемещена. Я также ищу общее решение.
Джртуртон
Конечно, вы все еще можете сохранить anchorPoint, моя точка зрения, вы не должны использовать его для измерений. Система автоматического размещения UIView должна быть независимой от преобразований CALayer. So UIView: макет, CALayer: внешний вид / анимация
Джон Эстропия
1
Так и должно быть, но это не так. Вы действительно пробовали это? Изменение точки привязки смещает положение слоя после того, как автоматический макет выполнит свою работу. Ваше решение не работает с модифицированной узловой точкой.
Джртуртон
Да, я могу использовать разные anchorPoints для моих представлений, изложенных с ограничениями. Ваш ответ viewDidLayoutSubviewsдолжен исправить это; positionвсегда идет вместе с anchorPoint. Мой ответ просто показывает, как определить ограничение для преобразования идентичности.
Джон Эстропия
0

Этот вопрос и ответы вдохновили меня на то, чтобы решить собственные проблемы с Autolayout и масштабированием, но с помощью scrollviews. Я создал пример моего решения на github:

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

Это пример UIScrollView с пользовательской страницей, полностью выполненной в AutoLayout и масштабируемой (CATransform3DMakeScale) при длительном нажатии и нажатии для увеличения. iOS 6 и 7 совместимы.

Hans
источник
0

Это большая тема, и я не прочитал все комментарии, но столкнулся с той же проблемой.

У меня был вид из XIB с autolayout. И я хотел обновить его свойство transform. Внедрение представления в представление контейнера не решает мою проблему, потому что автоматическое расположение действовало странно на представление контейнера. Вот почему я только что добавил второе представление контейнера, чтобы оно содержало представление контейнера, которое содержит мое представление и применяет к нему преобразования.

plamkata__
источник
0

tl; dr Допустим, вы изменили опорную точку на (0, 0). Опорная точка теперь в левом верхнем углу. Каждый раз, когда вы видите центр слова в автоматическом макете, вы должны думать, что слева вверху .

Когда вы настраиваете свою anchorPoint, вы просто меняете семантику AutoLayout. Автоматическая разметка не будет мешать вашей anchorPoint, и наоборот. Если вы этого не понимаете, у вас будет плохое время .


Пример:

Рисунок А. узловая точка модификации Нет

#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