如何使用故事板中的另一个界面构建来刷新autolayout

时间:2014-08-11 10:50:35

标签: storyboard autolayout nslayoutconstraint

我想通过在故事板中设计两个不同的界面来查看动画。当事件触发时,它会将自动布局约束(或帧)更改为目标布局。

有没有图书馆可以完成这项工作?如果不是,请阅读以下内容。

使用autolayout进行视图动画是一件容易的事。例如:

[self.view layoutIfNeeded];
[UIView animateWithDuration:5.0 animations:^{
    self.heightConstraint.constant = 200
    [self.view layoutIfNeeded];
}];

但是如果你想通过重置所有子视图的所有约束来改变整个布局,那么通过编程就不容易了。使用Interface Builder是使用相同视图对象构建完全不同的布局的更好选择。因为,

  1. Interface Builder将在运行应用程序之前检查您的约束冲突。
  2. 这是“你所看到的就是你得到的东西(WYSIWYG)。”
  3. 这是我的想法:

    1. 在Interface Builder中设计控制器视图的布局(原始布局)。可以通过keypathviewTag
    2. 获取视图中的视图对象
    3. 将Interface Builder中的视图控制器复制为目标布局,根据需要更改所有框架和约束。
    4. 在运行时,当事件触发布局动画时,它会实例化目标布局的视图控制器。然后,它使用目标布局中的视图对象的约束(通过keypathviewTag)替换呈现视图对象的约束。
    5. 这是我的代码:

      // 1. Add the keypath `view` for the viewController. Use NSSet to remove duplicated keypaths or view tags.
      NSSet *keySet = [NSSet setWithArray:[keypaths arrayByAddingObject:@"view"]];
      
      
      // 2. Build the dictionaries to fetch corresponding view objects in the source and destination view controllers (presenting view and template view) by a specific key (a keypath or a view tag).
      NSMutableDictionary *presentingViewsForKeypaths = [NSMutableDictionary dictionaryWithCapacity:[keypaths count]];
      NSMutableDictionary *templateViewsForKeypaths = [NSMutableDictionary dictionaryWithCapacity:[keypaths count]];
      
      [keySet enumerateObjectsUsingBlock:^(id key, BOOL *stop) {
      
          UIView *presentingViewForKeypath = nil;
          UIView *templateViewForKeypath = nil;
      
          // Fetch the view objects by the keypath or view tag.
          if ([key isKindOfClass:[NSString class]]) {
              presentingViewForKeypath = [self valueForKeyPath:key];
              templateViewForKeypath = [viewController valueForKeyPath:key];
      
          } else if ([key isKindOfClass:[NSNumber class]]) {
              presentingViewForKeypath = [self.view viewWithTag:[key integerValue]];
              templateViewForKeypath = [viewController.view viewWithTag:[key integerValue]];
      
          } else {
              NSString *errorMessage = [NSString stringWithFormat:@"keypath (%@) is not kind of NSString or NSNumber.", key];
              NSAssert(nil, errorMessage);
              NSLog(@"%@", errorMessage);
              return;
          }
      
          // The view objects should be found in both view controllers.
          if (!presentingViewForKeypath || !templateViewForKeypath) {
              NSString *errorMessage = [NSString stringWithFormat:@"KeynoteAnimation warning: keypath (%@) is not found.", key];
              NSAssert(nil, errorMessage);
              NSLog(@"%@", errorMessage);
              return;
          } else if (!templateViewForKeypath) {
              NSString *errorMessage = [NSString stringWithFormat:@"KeynoteAnimation warning: keypath (%@) is not found.", key];
              NSAssert(nil, errorMessage);
              NSLog(@"%@", errorMessage);
              return;
          }
      
          // Add the view objects to the corresponding dictionaries.
          presentingViewsForKeypaths[key] = presentingViewForKeypath;
          templateViewsForKeypaths[key] = templateViewForKeypath;
      }];
      
      
      // 3. Change the constraints of the view objects for the keys.
      NSSet *presentingViewsForKeypathsSet = [NSSet setWithArray:[presentingViewsForKeypaths allValues]];
      NSSet *templateViewsForKeypathsSet = [NSSet setWithArray:[templateViewsForKeypaths allValues]];
      NSMutableArray *keysShouldUpdateItsViewFrame = [NSMutableArray array];
      
      [keySet enumerateObjectsUsingBlock:^(id key, BOOL *stop) {
      
          NSLog(@"========= Remove constraints for Key: %@ ==========", key);
      
          __block BOOL didChangeAnyConstraint = NO;
      
          UIView *presentingView = presentingViewsForKeypaths[key];
          UIView *templateView = templateViewsForKeypaths[key];
      
          // Remove the original constraints for the view objects.
          [presentingView.constraints enumerateObjectsUsingBlock:^(NSLayoutConstraint *constraint, NSUInteger idx, BOOL *stop) {
      
              if ([presentingViewsForKeypathsSet containsObject:constraint.firstItem] &&
                  ([presentingViewsForKeypathsSet containsObject:constraint.secondItem] || !constraint.secondItem)) {
      
                  NSLog(@"detect constraint: %@\nFind: %@ (%x) -- %@ (%x)", constraint,  NSStringFromClass([constraint.firstItem class]), [constraint.firstItem hash], NSStringFromClass([constraint.secondItem class]), [constraint.secondItem hash]);
      
                  [presentingView removeConstraint:constraint];
      
                  didChangeAnyConstraint = YES;
              }
      
          }];
      
          NSLog(@"----------- Add constraints for Key: %@ -----------", key);
      
          // Add the new constraints for the view objects.
          [templateView.constraints enumerateObjectsUsingBlock:^(NSLayoutConstraint *constraint, NSUInteger idx, BOOL *stop) {
      
              if ([templateViewsForKeypathsSet containsObject:constraint.firstItem] &&
                  ([templateViewsForKeypathsSet containsObject:constraint.secondItem] || !constraint.secondItem)) {
                  NSLog(@"detect constraint: %@\nFind: %@ (%x) -- %@ (%x)", constraint, NSStringFromClass([constraint.firstItem class]), [constraint.firstItem hash], NSStringFromClass([constraint.secondItem class]), [constraint.secondItem hash]);
      
                  if ([constraint isKindOfClass:NSClassFromString(@"NSIBPrototypingLayoutConstraint")]) {
                      NSLog(@"|||||||||||||||||||| WARNING ||||||||||||||||");
                      //return; // Return here to filter out this constraint.
                  }
      
                  // Create the new NSLayoutConstraint for the presenting view object.
                  NSArray *firstItemKeys = [templateViewsForKeypaths allKeysForObject:constraint.firstItem];
                  NSArray *secondItemKeys = [templateViewsForKeypaths allKeysForObject:constraint.secondItem];
      
                  id firstItemKey = [firstItemKeys count]? firstItemKeys[0]:nil;
                  id secondItemKey = [secondItemKeys count]? secondItemKeys[0]:nil;
      
                  id firstItem = firstItemKey? presentingViewsForKeypaths[firstItemKey]:nil;
                  id secondItem = secondItemKey? presentingViewsForKeypaths[secondItemKey]:nil;
      
                  NSLog(@"params for new constraints: %@:%@(%x) => %@:%@(%x) for view: %@(%x)", firstItemKey, NSStringFromClass([firstItem class]), [firstItem hash], secondItemKey, NSStringFromClass([secondItem class]), [secondItem hash], NSStringFromClass([presentingView class]), [presentingView hash]);
      
                  NSLayoutConstraint *newLayoutConstraint = [NSLayoutConstraint constraintWithItem:firstItem attribute:constraint.firstAttribute relatedBy:constraint.relation toItem:secondItem attribute:constraint.secondAttribute multiplier:constraint.multiplier constant:constraint.constant];
      
                  [presentingView addConstraint:newLayoutConstraint];
      
                  didChangeAnyConstraint = YES;
              }
      
          }];
      
          // Record the view object which doesn't have any layout constraint. It may change its frame set in the storyboard.
          if (!didChangeAnyConstraint) {
              [keysShouldUpdateItsViewFrame addObject:key];
          }       
      }];
      
      
      // Start Animation
      [UIView animateWithDuration:1 delay:0 usingSpringWithDamping:1 initialSpringVelocity:1 options:0 animations:^{
      
          // Update the frame of the view objects which did not change its constraints (no constraint).
          [keysShouldUpdateItsViewFrame enumerateObjectsUsingBlock:^(id key, NSUInteger idx, BOOL *stop) {
      
              UIView *presentingView = presentingViewsForKeypaths[key];
              UIView *templateView = templateViewsForKeypaths[key];
      
              presentingView.frame = templateView.frame;
      
          }];
      
          [self.view layoutIfNeeded];
      
      } completion:^(BOOL finished) {
          NSLog(@"finish.");
      }];
      

      添加新约束时,如果约束的类为NSIBPrototypingLayoutConstraint,它将崩溃并显示消息:当添加到视图时,约束的项必须是该视图的后代(或者视图本身)。如果在组装视图层次结构之前需要解析约束,则会崩溃。

      我认为视图对象已经在视图层次结构中。我不知道如何解决这个问题。

      如果过滤掉这些约束,它将不会更新布局,我不确定是否满足约束。

0 个答案:

没有答案