#objective-c #constraints #core-animation #nsview
#objective-c #ограничения #ядро-анимация #nsview
Вопрос:
Я использую XCode 12, macOS, objective-c, а не iOS.
У меня есть меню (горизонтальный вид стека) с 3 пунктами:
Синее подчеркивание создается программно:
- (void)viewDidLoad {
[super viewDidLoad];
[self createAnimatedLineAtButton:self.importButton];
}
// Create line
- (void)createAnimatedLineAtButton:(NSButton*)button {
// This view is layer backed
SHViewCustomBackground* underlineView = [[SHViewCustomBackground alloc] init];
underlineView.backgroundColor = [NSColor colorNamed:@"color_spot"]; // blue color
underlineView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:underlineView];
self.underlineView = underlineView;
[self updateAnimatedLineUnderButton:self.importButton];
}
Следующие коды обновляют ограничения (первоначально вызываемые при создании строки выше), а затем при нажатии каждой кнопки.
- (void)updateAnimatedLineUnderButton:(NSButton *)button {
// Constraints are stored here
if (self.underlineConstraints) {
[self.view removeConstraints:self.underlineConstraints];
}
[NSAnimationContext runAnimationGroup:^(NSAnimationContext *context){
context.duration = 1;
context.allowsImplicitAnimation = YES;
NSView* underlineView = self.underlineView;
NSDictionary *views = NSDictionaryOfVariableBindings(underlineView, button);
self.underlineConstraints = [NSLayoutConstraint constraintsWithVisualFormat:@"V:[button]-[underlineView(1)]" options:NSLayoutFormatAlignAllLeading | NSLayoutFormatAlignAllTrailing metrics:nil views:views];
[self.view addConstraints:self.underlineConstraints];
[self.view layoutSubtreeIfNeeded];
} completionHandler:nil];
}
Вот действия кнопки
- (IBAction)buttonClicked:(id)sender {
[self updateAnimatedLineUnderButton:(NSButton*)sender];
}
Теперь мой вопрос: когда я нажимаю кнопки в таком порядке «Импорт»> «Экспорт»> «Импорт», строка анимируется, как и ожидалось, от импорта до экспорта и обратно. Всякий раз, когда я нажимаю на «Заполнитель» (средняя кнопка), строка просто прыгает и не анимируется. Когда я нажимаю «Импорт» или «Экспорт», после этого он тоже прыгает.
Я бьюсь головой о том, что здесь не так. Все кнопки подключены правильно, все действия выполняются правильно.
РЕДАКТИРОВАТЬ: после сравнения всех кнопок я обнаружил, что кнопка импорта и экспорта (где она работает) имеют одинаковую ширину.
Комментарии:
1. Я вижу, что ширина этого подчеркивания также изменяется в анимации, и обратите внимание на ваш комментарий о ширине. Может быть, выровнять по центру и установить ширину как два отдельных явных ограничения (или даже просто дать ему фиксированную ширину на данный момент) и посмотреть, как это происходит?
2. он работает с фиксированной шириной. даже ограничение выравнивания по центру в сочетании с «button-equalWidth» не работает. Всякий раз, когда дело доходит до динамической ширины, она показывает поведение в формате GIF. К сожалению, я хочу, чтобы динамическая ширина была локализована с разной длиной строки
3. Должно работать, если вы выполняете анимацию разделения — возможно, обновите свой код? Также, возможно, переместить layoutSubtree в блок завершения?
4. LayoutSubtree запускает анимацию … поэтому не может перейти к блоку завершения. Что вы подразумеваете под разделенной анимацией?
5. Я имею в виду, где вы анимируете ограничения ширины и выравнивания по центру отдельно.
Ответ №1:
Вот идея … это работает. Изначально я собирался сделать это по-своему, но, возможно, вам это не понравится, в итоге анимировал два ограничения. Но это работает хорошо.
Идея очень похожа на вашу, но разница здесь в том, что я создаю начальное и конечное ограничение и анимирую их вместо.
@implementation ViewController
- ( void ) viewDidLoad
{
super.viewDidLoad;
[self animateLine:self.longButton];
}
// Animate the line
- ( void ) animateLine:( NSButton * ) button
{
[NSAnimationContext runAnimationGroup: ^ ( NSAnimationContext * context ) {
context.duration = 1;
context.allowsImplicitAnimation = YES;
self.leadingConstraint.animator.constant = button.frame.origin.x;
self.trailingConstraint.animator.constant = self.stackView.bounds.size.width - button.frame.origin.x - button.frame.size.width;
NSLog ( @"Setting leading to %f trailing to %f", self.leadingConstraint.constant, self.trailingConstraint.constant );
self.lineField.layoutSubtreeIfNeeded;
}];
}
- ( IBAction ) buttonAction:( id ) sender
{
[self animateLine:sender];
}
Это оно. Остальное происходит в раскадровке. Я установил ограничения так, чтобы расстояния имели смысл, поменяв местами первый и второй элемент по вкусу.
Комментарии:
1. Это здорово. Хотя я хотел создать все в коде, а не в IB.
2. Он, он, он, тебе нравится страдать… Я сделал как можно больше в раскадровке, чтобы сэкономить время и протестировать идею. Именно поэтому я в конечном итоге сделал это таким образом, поскольку это было самым быстрым. Я думаю, что вы можете легко перенести это в код.
3. Я попытаюсь настроить ограничения для viewdidload и сохранить их в некоторых ссылочных указателях. Если это сработает, я определенно приму ваш ответ, поскольку он проверен на работоспособность. Спасибо