我正在开发一个由其他人启动的应用程序,我相信是在 iOS 5 发布时启动的。大多数 UI 都是代码生成的,不会显式使用自动布局和约束。但是,有一些(后来添加的)自包含 UI 元素,在 nib(xib) 中定义,内部使用自动布局,这些元素包含在应用程序的更高级别视图中(在代码中生成,不使用自动布局)。
一段代码生成这些自包含元素之一的合成图像,将该图像动画化,然后将该图像交换为实际的 UI 元素视图。各种这些自包含的 UI 元素(全部基于相同的基类,但具有自己的笔尖 (xib))可以传递到此例程中,按照说明显示它们。其中大多数在 iOS 7 和 iOS 8 中运行良好,可以在屏幕上显示动画。
然而,一个特定的 UI 元素(仅在 iOS8 上)可以正确显示动画,但在动画的完成部分完成后,它会重置其位置,使其中心位于 0,0。这个在 iOS 7 上运行得很好。
我比较了与该元素配合良好的元素的代码和笔尖,但在有问题的 UI 元素的创建和内部组成方面没有看到任何不同的做法。
我在 @"center" 键路径的视图中添加了一个键路径观察器,这样我就可以看到是谁导致了这种重新居中,并且它似乎在自动布局系统代码中——我的代码都不在回溯中。
#0 0x002ed826 in -[MyFundsManager observeValueForKeyPath:ofObject:change:context:] at /Users/me/Dev/MyApp/iosapp/MyApp/MyFundsManager.m:158
#1 0x24b30372 in NSKeyValueNotifyObserver ()
#2 0x24b3001e in NSKeyValueDidChange ()
#3 0x24b1c840 in -[NSObject(NSKeyValueObserverNotification) didChangeValueForKey:] ()
#4 0x27463738 in -[UIView(Geometry) _applyISEngineLayoutValues] ()
#5 0x273898ee in -[UIView(Geometry) _resizeWithOldSuperviewSize:] ()
#6 0x23dff6ee in __53-[__NSArrayM enumerateObjectsWithOptions:usingBlock:]_block_invoke ()
#7 0x23dff5ea in -[__NSArrayM enumerateObjectsWithOptions:usingBlock:] ()
#8 0x2737a54a in -[UIView(Geometry) resizeSubviewsWithOldSize:] ()
#9 0x2746389c in -[UIView(AdditionalLayoutSupport) _is_layout] ()
#10 0x2760dbd4 in -[UIView(Hierarchy) _updateConstraintsAsNecessaryAndApplyLayoutFromEngine] ()
#11 0x27372b36 in -[UIView(CALayerDelegate) layoutSublayersOfLayer:] ()
#12 0x26d9accc in -[CALayer layoutSublayers] ()
#13 0x26d966b4 in CA::Layer::layout_if_needed(CA::Transaction*) ()
#14 0x26d9653c in CA::Layer::layout_and_display_if_needed(CA::Transaction*) ()
#15 0x26d95f20 in CA::Context::commit_transaction(CA::Transaction*) ()
#16 0x26d95d24 in CA::Transaction::commit() ()
#17 0x2736afc4 in _afterCACommitHandler ()
#18 0x23e9e5cc in __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ ()
#19 0x23e9bc8a in __CFRunLoopDoObservers ()
#20 0x23e9c092 in __CFRunLoopRun ()
#21 0x23dea620 in CFRunLoopRunSpecific ()
#22 0x23dea432 in CFRunLoopRunInMode ()
#23 0x2b1980a8 in GSEventRunModal ()
#24 0x273d4808 in UIApplicationMain ()
#25 0x0003e3fc in main at /Users/me/Dev/MyApp/iosapp/MyApp/main.m:17
下面的代码对传入的自包含 UI 元素进行动画处理,该元素是一个自包含视图控制器和视图,其内部组合通过笔尖和自动布局实现。
“inView”是传入的视图,应包含 UI 元素(UI 元素的父视图)。 “payPage”是自包含 UI 元素的视图控制器。
CGRect payRect = CGRectMake(windowSize.width/2 - pageSize.width/2, windowSize.height/2 - pageSize.height/2 - offset, pageSize.width, pageSize.height);
UIGraphicsBeginImageContext(payPage.view.bounds.size);
[[[payPage view] layer] renderInContext:UIGraphicsGetCurrentContext()];
//[[payPage view] drawViewHierarchyInRect:payPage.view.bounds afterScreenUpdates:YES];// iOS8 issues with this -- jumps and flickers
UIImage *compositeImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIImageView *compositeImageView = [[UIImageView alloc] initWithImage:compositeImage];
[[payPage view] setAlpha:0.0f];
UIViewController *parentVC = [self visibleVC];
_parentVC = parentVC;
UIView *inView = [(MyStandardViewController *)parentVC localView];
CGRect middleRect = CGRectZero;
if (nil == fromView)
{
middleRect.origin = [[[UIApplication sharedApplication] keyWindow] center];
}
else
{
middleRect.origin = [[fromView superview] convertPoint:[fromView center] toView:inView];
}
[compositeImageView setFrame:middleRect];
_blockingView = [[UIView alloc] initWithFrame:[inView frame]];
[_blockingView setAlpha:0.0f];
[_blockingView setBackgroundColor:BLOCKINGVIEWBACKGROUNDCOLOR];
[inView addSubview:_blockingView];
[inView addSubview:compositeImageView];
[UIView animateWithDuration:2.25f animations:^{
[compositeImageView setFrame:payRect];
[_blockingView setAlpha:1.0f];
}
completion:^(BOOL finished) {
[inView addSubview:[payPage view]];
if (nil != parentVC)
{
[parentVC addChildViewController:payPage];
}
[[payPage view] setFrame:payRect];
[[payPage view] setAlpha:1.0f];
[compositeImageView removeFromSuperview];
}
];