iOS进阶之旅-视图控制器控制与转换

来源:互联网 发布:苹果keynote在windows 编辑:程序博客网 时间:2024/04/29 23:41

iOS中控制器协调了后台数据与屏幕上展现的界面。在一个好的MVC设计模式中,控制器中用来处理很多必不可少的应用程序逻辑。UIKit提供了许多内置的视图控制器如UITabBarController或UINavigationController视图控制器。有了这些,正常开放中我们可以轻易的搭建出app的视图控制器框架。但有时候我们需要或者想要更多自定义的东西,本文将讨论一些有用的技术,视图控制器控制与转换transitionFromViewController:toViewController:duration:options:animations:completion的使用

视图控制器的切换

子视图控制器之间的转换可以很方便的,UINavigationController有一个漂亮的幻灯片动画在视图控制器间过渡,但或许你想定义自己的酷炫动画来切换子控制器视图

自定义视图子视图控制器的切换动画可以很方便的用下面的方法来实现(定义在UIViewController)

- (void)transitionFromViewController:(UIViewController *)fromViewController toViewController:(UIViewController *)toViewController duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion;
该方法在视图层次结构中添加并显示新的视图控制器,且可以为过渡过程指定一个自定义的动画效果,下面的代码演示了如何使用这个方法

1
2
3
4
5
6
7
8
9
10
11
12
13
UIViewController *nextViewController = [UIViewController new];

// Containment
[self addChildViewController:nextViewController];
[self.currentChildViewController willMoveToParentViewController:nil];

[self transitionFromViewController:self.currentViewController toViewController:nextViewController duration:_animationDuration.value options:0 animations:^{
    // Do any fancy animation you like
} completion:^(BOOL finished) {
    [nextViewController didMoveToParentViewController:self];
    [self.currentChildViewController removeFromParentViewController];
    self.currentChildViewController = nextViewController;
}];
一个demo

现在你知道子试图控制器的转换工作,让我们来建立一个基本的示例程序来深入探索下这个方法。

打开xcode,在storyboard中拖拽如下控件,并与viewController拖线建立联系

1
2
3
4
5
@property (strong, nonatomic) IBOutlet UISegmentedControl *transitionStyle;
@property (strong, nonatomic) IBOutlet UISlider *velocity;
@property (strong, nonatomic) IBOutlet UISlider *alpha;
@property (strong, nonatomic) IBOutlet UISlider *scale;
@property (strong, nonatomic) IBOutlet UISlider *animationDuration;


维护一个枚举,用来标识segmentControl的选择(segmentControl用来选择动画的类型)

1
2
3
4
5
6
7
8
// These values align with the segments in the segmented control names "transitionStyle"
typedef NS_ENUM(NSInteger, ViewControllerTransition) {
    ViewControllerTransitionSlideFromTop   = 0,
    ViewControllerTransitionSlideFromLeft,
    ViewControllerTransitionSlideFromBottom,
    ViewControllerTransitionSlideFromRight,
    ViewControllerTransitionRotateFromRight
};
一个随机颜色的方法

1
2
3
4
5
6
7
8
// https://gist.github.com/kylefox/1689973
UIColor* color()
{
    CGFloat hue = ( arc4random() % 256 / 256.0 );  //  0.0 to 1.0
    CGFloat saturation = ( arc4random() % 128 / 256.0 ) + 0.5;  //  0.5 to 1.0, away from white
    CGFloat brightness = ( arc4random() % 128 / 256.0 ) + 0.5;  //  0.5 to 1.0, away from black
    return [UIColor colorWithHue:hue saturation:saturation brightness:brightness alpha:1];
}
我们需要一个私有的,指向当前显示的子视图控制器的引用

1
2
3
4
5
@interface ViewController ()

@property (nonatomic, weak) UIViewController *currentChildViewController;

@end
接下来,我们声明一个nextViewController方法,这个方法会创建被我们用做子控制器的UIViewController对象。我们需要对其进行一些特殊的处理,从而使我们的程序逻辑看起来更清晰

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
- (UIViewController *)nextViewController
{
    UIViewController *viewController = [UIViewController new];
    viewController.view.frame = CGRectInset(self.view.bounds, 0200);
    UILabel *label = [[UILabel alloc] initWithFrame:viewController.view.bounds];
    label.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
    label.textColor = [UIColor whiteColor];
    label.backgroundColor = [UIColor clearColor];
    label.textAlignment = NSTextAlignmentCenter;
    label.numberOfLines = 0;
    label.text = @"Contained View Controller's View\n\nClick To Transition";
    [viewController.view addSubview:label];

    viewController.view.backgroundColor = color();
    viewController.view.layer.borderWidth = 6;
    viewController.view.layer.cornerRadius = 8;
    viewController.view.layer.borderColor = color().CGColor;
    viewController.view.layer.shadowColor = [UIColor blackColor].CGColor;
    viewController.view.layer.shadowOffset = CGSizeZero;
    viewController.view.layer.shadowOpacity = 0.5;

    UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tap:)];
    [viewController.view addGestureRecognizer:tap];

    return viewController;
}
tap手势事件方法的实现

1
2
3
4
5
- (void)tap:(UIGestureRecognizer *)gr
{
    if (gr.state == UIGestureRecognizerStateEnded)
        [self transitionToNextViewController];
}
接着,我们需要实现transitionToNextViewController,在这个方法中我们队系统的API进行封装,以方便我们的调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
- (void)transitionToNextViewController
{
    UIViewController *nextViewController = [self nextViewController];
    // Containment
    [self addChildViewController:nextViewController];
    [self.currentChildViewController willMoveToParentViewController:nil];
    nextViewController.view.transform = [self startingTransformForViewControllerTransition:self.transitionStyle.selectedSegmentIndex];
    [self transitionFromViewController:self.currentChildViewController toViewController:nextViewController duration:_animationDuration.value options:0 animations:^{
        self.currentChildViewController.view.alpha = _alpha.value;
        CGAffineTransform transform = CGAffineTransformMakeTranslation(-nextViewController.view.transform.tx * _velocity.value, -nextViewController.view.transform.ty * _velocity.value);
        transform = CGAffineTransformRotate(transform, acosf(nextViewController.view.transform.a));
        self.currentChildViewController.view.transform = CGAffineTransformScale(transform, _scale.value, _scale.value);
        nextViewController.view.transform = CGAffineTransformIdentity;
    } completion:^(BOOL finished) {
        [nextViewController didMoveToParentViewController:self];
        [self.currentChildViewController removeFromParentViewController];
        self.currentChildViewController = nextViewController;
    }];
}
startingTransformForViewControllerTransition:的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
- (CGAffineTransform)startingTransformForViewControllerTransition:(ViewControllerTransition)transition
{
    CGFloat width = CGRectGetWidth(self.view.bounds);
    CGFloat height = CGRectGetHeight(self.view.bounds);
    CGAffineTransform transform = CGAffineTransformIdentity;

    switch (transition)
    {
        case ViewControllerTransitionSlideFromTop:
            transform = CGAffineTransformMakeTranslation(0-height);
            break;
        case ViewControllerTransitionSlideFromLeft:
            transform = CGAffineTransformMakeTranslation(-width, 0);
            break;
        case ViewControllerTransitionSlideFromRight:
            transform = CGAffineTransformMakeTranslation(width, 0);
            break;
        case ViewControllerTransitionSlideFromBottom:
            transform = CGAffineTransformMakeTranslation(0, height);
            break;
        case ViewControllerTransitionRotateFromRight:
            transform = CGAffineTransformMakeTranslation(width, 0);
            transform = CGAffineTransformRotate(transform, M_PI);
            break;
        default:
            break;
    }

    return transform;
}
设置当前视图控制器的shadowPath以提升性能

1
2
3
4
5
6
- (void)viewDidLayoutSubviews
{
    [super viewDidLayoutSubviews];

    self.currentChildViewController.view.layer.shadowPath = [UIBezierPath bezierPathWithRoundedRect:self.currentChildViewController.view.bounds cornerRadius:8].CGPath;
}
最后,我们只需要在viewDidLoad中添加并声明一个初始的viewController


1
2
3
4
5
6
7
8
9
10
11
12
13
- (void)viewDidLoad
{
    [super viewDidLoad];

    // Add an initial contained viewController
    UIViewController *viewController = [self nextViewController];

    // Contain the view controller
    [self addChildViewController:viewController];
    [self.view addSubview:viewController.view];
    [viewController didMoveToParentViewController:self];
    self.currentChildViewController = viewController;
}

transitionFromViewController:是iOS5为我们提供的自定义视图控制器切换动画的API,这个方法主要用于父viewController在切换显示不同childViewController时,实现自定义动画效果。以上结论是我通过网上查阅资料,并对自己工作过程中的使用情况进行的总结。有不对的地方欢迎大家指正。

邮箱 angxian66@163.com 或在本文下留言

1 0