[EDIT: Попередження: вся наступна дискусія, можливо, застаріла або, принаймні, сильно пом'якшиться за допомогою iOS 8, що більше не може зробити помилку запускати макет під час застосування трансформації перегляду.]
Автоматичний розклад проти перегляду трансформацій
Автовипуск зовсім не добре грає з перетвореннями перегляду. Наскільки я можу помітити, причина полягає в тому, що ви не маєте возитися з кадром виду, який має перетворення (крім трансформації ідентичності за замовчуванням) - але саме це робить авторозкладка. Те, як працює авторозкладка, полягає в тому, що під layoutSubviews
час виконання відбувається обмеження всіх обмежень і відповідно встановлення кадрів усіх переглядів.
Іншими словами, обмеження не є магічними; вони просто список справ. layoutSubviews
це те, де виконується список справ. І це робиться, встановлюючи кадри.
Я не можу не сприймати це як помилку. Якщо застосувати це перетворення до подання:
v.transform = CGAffineTransformMakeScale(0.5,0.5);
Я очікую, що вид буде з центром у тому самому місці, що і раніше, і в половині розміру. Але залежно від її обмежень це може бути зовсім не тим, що я бачу.
[Насправді тут є друга несподіванка: застосування трансформації до виду запускає макет негайно. Це здається мені ще однією помилкою. А може, це серце першого клопа. Я б очікував, що я можу піти з трансформацією принаймні до моменту розміщення, наприклад, пристрій повернутись так само, як я можу піти з анімацією кадрів до моменту компонування. Але насправді час компонування негайний, що здається просто неправильним.]
Рішення 1: Без обмежень
Одне з поточних рішень полягає в тому, якщо я збираюся застосувати напівперманентне перетворення до подання (а не просто тимчасово помахувати ним), щоб усунути всі обмеження, що впливають на нього. На жаль, це зазвичай призводить до того, що подання зникає з екрану, оскільки авторозкладка все ще має місце, і тепер немає обмежень, щоб сказати нам, куди слід подати подання. Отже, крім усунення обмежень, я встановив вигляд " translatesAutoresizingMaskIntoConstraints
ТАК". Перегляд тепер працює за старим способом, фактично не впливаючи на авторозкладку. (Очевидно, на це впливає авторозмітка, але неявні обмеження автоматичної маски призводять до того, що її поведінка є такою, якою вона була до автоматичного розкладу.)
Рішення 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: Замість цього використовуйте шарові перетворення
Замість перетворень перегляду використовуйте перетворення шарів, які не запускають макет і, таким чином, не викликають негайного конфлікту з обмеженнями.
Наприклад, ця проста анімація "пульсуючого" перегляду цілком може зламатися при автоматичному розкладі:
[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];
layerView
дізнатися його ширину? Чи приклеює свою праву сторону до чогось іншого?