Ответ 1
TL; DR
Я сделал категорию на UIViewController
, которая, надеюсь, исправляет эту проблему для вас. Я не могу воспроизвести повреждение навигационной панели на устройстве, но я могу сделать это на симуляторе довольно часто, и эта категория решает проблему для меня. Надеюсь, он также решит его для вас на устройстве.
Проблема и решение
На самом деле я точно не знаю, что именно вызывает это, но анимация слоев на панели навигации кажется, что либо выполняется дважды, либо не полностью завершает или... что-то. Во всяком случае, я обнаружил, что вы можете просто добавить некоторые анимации в эти подпрограммы, чтобы вернуть их туда, где они должны быть (с правильной непрозрачностью, цветом и т.д.). Хитрость состоит в том, чтобы использовать ваш объект управления transitionCoordinator
и подключаться к нескольким событиям, а именно к событию, которое происходит, когда вы поднимаете палец вверх, а распознаватель интерактивного распознавания поцелуя заканчивается, а остальная часть анимации начинается, а затем событие это происходит, когда неинтерактивная половина анимации заканчивается.
Вы можете подключиться к этим событиям, используя пару методов на transitionCoordinator
, в частности notifyWhenInteractionEndsUsingBlock:
и animateAlongsideTransition:completion:
. В первом мы создаем копии всех текущих анимаций слоев subviews navbar, немного модифицируем их и сохраняем их, чтобы мы могли применять их позже, когда неинтерактивная часть анимации заканчивается, которая находится в блоке завершения последнего из этих двух методов.
Резюме
- Слушайте, когда заканчивается интерактивная часть перехода.
- Собирайте анимации для всех слоев вида в панели навигации
- Скопируйте и измените эти анимации немного (установите fromValue на то же, что и forValue, установите длительность на ноль и несколько других вещей)
- Слушайте, когда неинтерактивная часть перехода заканчивается.
- Примените скопированные/модифицированные анимации к слоям просмотров
код
И вот код для категории UIViewController
:
@interface UIViewController (FixNavigationBarCorruption)
- (void)fixNavigationBarCorruption;
@end
@implementation UIViewController (FixNavigationBarCorruption)
/**
* Fixes a problem where the navigation bar sometimes becomes corrupt
* when transitioning using an interactive transition.
*
* Call this method in your view controller viewWillAppear: method
*/
- (void)fixNavigationBarCorruption
{
// Get our transition coordinator
id<UIViewControllerTransitionCoordinator> coordinator = self.transitionCoordinator;
// If we have a transition coordinator and it was initially interactive when it started,
// we can attempt to fix the issue with the nav bar corruption.
if ([coordinator initiallyInteractive]) {
// Use a map table so we can map from each view to its animations
NSMapTable *mapTable = [[NSMapTable alloc] initWithKeyOptions:NSMapTableStrongMemory
valueOptions:NSMapTableStrongMemory
capacity:0];
// This gets run when your finger lifts up while dragging with the interactivePopGestureRecognizer
[coordinator notifyWhenInteractionEndsUsingBlock:^(id<UIViewControllerTransitionCoordinatorContext> context) {
// Loop through our nav controller nav bar subviews
for (UIView *view in self.navigationController.navigationBar.subviews) {
NSArray *animationKeys = view.layer.animationKeys;
NSMutableArray *anims = [NSMutableArray array];
// Gather this view animations
for (NSString *animationKey in animationKeys) {
CABasicAnimation *anim = (id)[view.layer animationForKey:animationKey];
// In case any other kind of animation somehow gets added to this view, don't bother with it
if ([anim isKindOfClass:[CABasicAnimation class]]) {
// Make a pseudo-hard copy of each animation.
// We have to make a copy because we cannot modify an existing animation.
CABasicAnimation *animCopy = [CABasicAnimation animationWithKeyPath:anim.keyPath];
// CABasicAnimation properties
// Make sure fromValue and toValue are the same, and that they are equal to the layer final resting value
animCopy.fromValue = [view.layer valueForKeyPath:anim.keyPath];
animCopy.toValue = [view.layer valueForKeyPath:anim.keyPath];
animCopy.byValue = anim.byValue;
// CAPropertyAnimation properties
animCopy.additive = anim.additive;
animCopy.cumulative = anim.cumulative;
animCopy.valueFunction = anim.valueFunction;
// CAAnimation properties
animCopy.timingFunction = anim.timingFunction;
animCopy.delegate = anim.delegate;
animCopy.removedOnCompletion = anim.removedOnCompletion;
// CAMediaTiming properties
animCopy.speed = anim.speed;
animCopy.repeatCount = anim.repeatCount;
animCopy.repeatDuration = anim.repeatDuration;
animCopy.autoreverses = anim.autoreverses;
animCopy.fillMode = anim.fillMode;
// We want our new animations to be instantaneous, so set the duration to zero.
// Also set both the begin time and time offset to 0.
animCopy.duration = 0;
animCopy.beginTime = 0;
animCopy.timeOffset = 0;
[anims addObject:animCopy];
}
}
// Associate the gathered animations with each respective view
[mapTable setObject:anims forKey:view];
}
}];
// The completion block here gets run after the view controller transition animation completes (or fails)
[coordinator animateAlongsideTransition:nil completion:^(id<UIViewControllerTransitionCoordinatorContext> context) {
// Iterate over the mapTable keys (views)
for (UIView *view in mapTable.keyEnumerator) {
// Get the modified animations for this view that we made when the interactive portion of the transition finished
NSArray *anims = [mapTable objectForKey:view];
// ... and add them back to the view layer
for (CABasicAnimation *anim in anims) {
[view.layer addAnimation:anim forKey:anim.keyPath];
}
}
}];
}
}
@end
А затем просто вызовите этот метод в своем методе контроллера viewWillAppear:
(в вашем случае тестового проекта это будет класс ViewController
):
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self fixNavigationBarCorruption];
}