IOS / Objective-C: пользовательский эффект Segue на панели навигации по сравнению с Show Segue - PullRequest
0 голосов
/ 11 сентября 2018

У меня есть два контроллера вида, где я использую обычный Show Segue (справа налево), чтобы идти в одном направлении, и пользовательский segue (слева направо), чтобы идти в другом направлении. Я не думаю, что я должен делать раскручивание, потому что ни один VC не подчинен другому, и использование этих сегментов означает, что один навигационный контроллер управляет всем.

В верхнем левом углу обоих VC у меня есть общая кнопка BarButton, содержащая фотографию профиля.

При использовании обычного перехода справа налево фотография профиля на кнопке панели остается неизменной. Это выглядит великолепно, когда движется остальная часть экрана, но элемент, общий для обоих, фотография профиля остается на месте.

В другом направлении (слева направо), однако, используя пользовательский переход, весь экран VC, включая панель навигации, включается, и вы в основном видите, что фотография профиля приходит с левого края, прежде чем упасть в обычную левую панель. Положение кнопки, где та же фотография была моментом ранее. Это выглядит плохо.

Есть ли способ заставить общий элемент на панели навигации оставаться на месте во время пользовательского перехода, чтобы лучше имитировать поведение системы show segue?

Заранее спасибо за любые предложения.

Вот мой код для пользовательского перехода:

#import "customSegue.h"
#import "QuartzCore/QuartzCore.h"

@implementation customSegue

-(void)perform {

    UIViewController *destinationController = (UIViewController*)[self destinationViewController];

    UIViewController *sourceViewController = (UIViewController*)[self sourceViewController];
    CGFloat animationDuration = .40;  
    transition.duration = animationDuration;
    transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault];
    transition.type = kCATransitionMoveIn;  
    transition.subtype = kCATransitionFromLeft;  

    [sourceViewController.navigationController.view.layer addAnimation:transition
                                                                forKey:kCATransition];

    UIColor *previousWindowBackgroundColor = sourceViewController.view.window.backgroundColor;

    sourceViewController.view.window.backgroundColor = destinationController.view.backgroundColor;


    [sourceViewController.navigationController pushViewController:destinationController animated:NO];
    // switch the window color back after the transition duration from above
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(animationDuration * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        // make sure we still have a handle on the destination controller
        if (destinationController) {
            destinationController.view.window.backgroundColor = previousWindowBackgroundColor;
        }
    });

}

@end

1 Ответ

0 голосов
/ 12 сентября 2018

Здравствуйте еще раз @ user6631314

Не думаю, что вы получите желаемое, применив CATransition и прикрепив его к слою представления navigationController.

Вместо этого я бы порекомендовал сделать представляющий ViewController делегатом для UINavigationController и использовать свою собственную логику для нажатия LTR (с навигационной панелью будет намного плавнее, и вам больше не придется беспокоиться об этой черной полосе во время перехода, который мой предыдущий ответ помог вам решить / обойти).

Таким образом, для этого вам нужно установить контроллер представления (или некоторый контроллер представления координатора) как UINavigationControllerDelegate и реализовать этот метод:

- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
                                  animationControllerForOperation:(UINavigationControllerOperation)operation
                                               fromViewController:(UIViewController*)fromVC
                                                 toViewController:(UIViewController*)toVC

В этом методе вы захотите проверить, является ли операция UINavigationControllerOperationPush, и если это так, вернуть подкласс NSObject, соответствующий протоколу UIViewControllerAnimatedTransitioning (в противном случае верните nil, чтобы все остальные операции навигации были стандартными). , Внутри этого класса вы будете обрабатывать переопределенную анимацию push-анимации контроллера навигации.

Основная логика для push-анимации LTR заключается в том, что вы хотите запустить toView с экрана влево, а затем анимировать его так, чтобы он полностью отображался на экране после продолжительности анимации (0,4 из кода) - поэтому запустите Смещение позиции x к отрицательному значению ширины вида (так что оно полностью за кадром), затем во время анимации установите позицию x на 0 (или вы можете просто + = ширина представления).

Вот пример того, как выглядит текущая пользовательская реализация segue (обратите внимание, что панель навигации тоже скользит, о чем вы и пишите здесь):

enter image description here

И переход с помощью настраиваемого контроллера анимации:

enter image description here

Вот полный код:

#import "ViewController.h"
#import "LTRPushAnimator.h"

@interface ViewController () < UINavigationControllerDelegate>
@property (strong, nullable) UIView *profileView;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.navigationController.delegate = self;
    self.navigationItem.hidesBackButton = YES;
    self.profileView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 40, 40)];
    UIImageView *profileImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"Homer.jpg"]];
    [profileImageView setFrame:CGRectMake(0, 0, 40, 40)];
    profileImageView.layer.cornerRadius = 20.0f;
    profileImageView.layer.masksToBounds = YES;
    profileImageView.clipsToBounds = YES;
    profileImageView.layer.borderColor = [UIColor blackColor].CGColor;
    profileImageView.layer.borderWidth = 1.0f;
    [self.profileView addSubview:profileImageView];
    UIBarButtonItem *lbi = [[UIBarButtonItem alloc] initWithCustomView:self.profileView];
    self.navigationItem.leftBarButtonItem = lbi;
    // Do any additional setup after loading the view, typically from a nib.
}

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
}

- (IBAction)pushFakeViewController:(id)sender {
    UIViewController *fakeViewController = [[UIViewController alloc] init];
    fakeViewController.view.backgroundColor = [UIColor redColor];
    UIBarButtonItem *lbi = [[UIBarButtonItem alloc] initWithCustomView:self.profileView];
    fakeViewController.navigationItem.leftBarButtonItem = lbi;
    fakeViewController.navigationItem.hidesBackButton = YES;
    [self.navigationController pushViewController:fakeViewController animated:YES];
    // this is just in here to pop back to the root view controller since we removed the back button, it can be removed obviously
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [self.navigationController popViewControllerAnimated:YES];
    });
}

- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
                                  animationControllerForOperation:(UINavigationControllerOperation)operation
                                               fromViewController:(UIViewController*)fromVC
                                                 toViewController:(UIViewController*)toVC
{
    if (operation == UINavigationControllerOperationPush) {
        return [[LTRPushAnimator alloc] init];
    }
    // otherwise standard animation
    return nil;
}

@end

LTRPushAnimator.h

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN

@interface LTRPushAnimator : NSObject <UIViewControllerAnimatedTransitioning>

@end

NS_ASSUME_NONNULL_END

LTRPushAnimator.m

#import "LTRPushAnimator.h"

@implementation LTRPushAnimator

- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext
{
    return 0.4;
}

- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
    [self _animatePushByFrameWithContext:transitionContext];
}

- (void)_animatePushByFrameWithContext:(id<UIViewControllerContextTransitioning>)transitionContext {
    UIViewController *toViewController   = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    CGRect toVCFrame = toViewController.view.frame;
    CGFloat viewWidth = toVCFrame.size.width;
    toVCFrame.origin.x -= viewWidth;
    [toViewController.view setFrame:toVCFrame];
    [[transitionContext containerView] addSubview:toViewController.view];
    [UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
        CGRect finalVCFrame = toViewController.view.frame;
        finalVCFrame.origin.x = 0;
        [toViewController.view setFrame:finalVCFrame];
    } completion:^(BOOL finished) {
        [transitionContext completeTransition:![transitionContext transitionWasCancelled]];
    }];
}

@end
...