自定义转场

效果图 Gif.gif

设备支持的翻转有哪些 , 程序在设备左转再左转之后打印出的日志是程序方向不变 , 即:设备倒向 , 程序不是倒向反而还是左向;
transitioningDelegate:告诉系统我的转场动画要自定义而不是默认;
UIViewControllerAnimatedTransitioning:两个代理方法:
- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext

AppDelegate

#import "AppDelegate.h"
#import "TestViewController.h"
#import "CustomNavigationControllerDelegate.h"

@interface AppDelegate ()
{
    CustomNavigationControllerDelegate *_customNavigationDelegate;
}
@end

@implementation AppDelegate


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    self.window.backgroundColor = [UIColor whiteColor];
    TestViewController *testVC = [[TestViewController alloc] init];
    UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:testVC];
    _customNavigationDelegate = [[CustomNavigationControllerDelegate alloc] init];
    nav.delegate = _customNavigationDelegate;
    self.window.rootViewController = nav;
    [self.window makeKeyAndVisible];
    return YES;
    
}

@end

TestViewController.m

#import "TestViewController.h"
#import "TestSecondViewController.h"
#import "PresentAnimation.h"
#import "DismissAnimation.h"


@interface TestViewController () <UIViewControllerTransitioningDelegate , TestSecondViewControllerDelegate>
{
    UIButton *_button;
    UITextField *_textField;
    PresentAnimation *_presentAnimation;
    DismissAnimation *_dismissAnimation;
    
    UIButton *_pushButton;
}

@end

@implementation TestViewController


/**
 *  设备支持的翻转有哪些 , 程序在设备左转再左转之后打印出的日志是程序方向不变 , 即:设备倒向 , 程序不是倒向反而还是左向;
 
 */
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
    //默认的是:UIInterfaceOrientationMaskAllButUpsideDown , 所以在屏幕倒向的时候程序仍然是左向;
    
    NSUInteger mask = [super supportedInterfaceOrientations];
    if (mask == UIInterfaceOrientationMaskAll) {
        NSLog(@"mask all");
    } else if (mask == UIInterfaceOrientationMaskAllButUpsideDown) {
        NSLog(@"mask all but upsidedown");
    }
    //支持全部方向:
    return UIInterfaceOrientationMaskAll;
    
}


#pragma mark - 防止设备旋转的过程中状态栏消失
- (BOOL)prefersStatusBarHidden {
    return NO;
}

#pragma mark - init
- (instancetype)init
{
    self = [super init];
    if (self) {
        //UIDeviceOrientationDidChangeNotification通知:
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(deviceOrientationChanged:) name:UIDeviceOrientationDidChangeNotification object:nil];
        //UIApplicationDidChangeStatusBarOrientationNotification通知:
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(statusBarOrientationChanged:) name:UIApplicationDidChangeStatusBarOrientationNotification object:nil];
        //键盘变换通知:
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDidShow:) name:UIKeyboardDidShowNotification object:nil];
        _presentAnimation = [[PresentAnimation alloc] init];
        _dismissAnimation = [[DismissAnimation alloc] init];
    }
    return self;
}

#pragma mark - 生命周期
- (void)viewDidLoad {
    [super viewDidLoad];
    
    _button = [[UIButton alloc] init];
    [_button setBackgroundColor:[UIColor orangeColor]];
    _button.frame = CGRectMake(100, 100, 150, 50);
    [_button setTitle:@"Present Button" forState:UIControlStateNormal];
    [_button addTarget:self action:@selector(presentButtonClick:) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:_button];
    
    
    _pushButton = [[UIButton alloc] init];
    [_pushButton setBackgroundColor:[UIColor orangeColor]];
    _pushButton.frame = CGRectMake(100, 200, 150, 50);
    [_pushButton setTitle:@"Push Button" forState:UIControlStateNormal];
    [_pushButton addTarget:self action:@selector(pushButtonClick:) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:_pushButton];
    
    
    
    _textField = [[UITextField alloc] init];
    _textField.backgroundColor = [UIColor lightGrayColor];
    [_textField setText:@"text"];
    _textField.frame = CGRectMake(100, 300, 200, 30);
    [self.view addSubview:_textField];
}


#pragma mark - PUSH & POP 视图
- (void)pushButtonClick:(id)sender {
    //initWithFromPush:方法:
    TestSecondViewController *testSecondVC = [[TestSecondViewController alloc] initWithFromPush:YES];
    [self.navigationController pushViewController:testSecondVC animated:YES];
}


#pragma mark - 模态视图
- (void)presentButtonClick:(id)sender {
    TestSecondViewController *modalVC = [[TestSecondViewController alloc] init];
    modalVC.delegate = self;
    //三要素:
    //1.告诉系统我的转场动画要自定义而不是默认:
    modalVC.transitioningDelegate = self;
    //这个style控制着模态视图跳转到新视图后原来视图的内容是否保留的操作:默认 UIModalPresentationFullScreen 不保留 , 在这里选择 UIModalPresentationCustom 可以保留:
    modalVC.modalPresentationStyle = UIModalPresentationCustom;
    [self presentViewController:modalVC animated:YES completion:nil];
}



#pragma mark - <TestSecondViewControllerDelegate>
- (void)testSecondViewControllerDidDismiss:(TestSecondViewController *)testSecondViewController {
    [self dismissViewControllerAnimated:YES completion:nil];
}


#pragma mark - <UIViewControllerTransitioningDelegate>
//自定义转场代理方法:
- (id<UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source
{
    //因为返回值是 id<UIViewControllerAnimatedTransitioning> 所以我要创建object对象去实现 UIViewControllerAnimatedTransitioning 即可!
    
    if ([presented isKindOfClass:[TestSecondViewController class]]) {
        return _presentAnimation;
    }
    return nil;
    
}

- (id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed
{
    if ([dismissed isKindOfClass:[TestViewController class]]) {
        return _dismissAnimation;
    }
    return nil;
    
}


#pragma mark - <UIDeviceOrientationDidChangeNotification>
- (void)deviceOrientationChanged:(NSNotification *)noti {
    NSLog(@"#1 receive device orientation changed notification");
    NSLog(@"%@", [self descriptionForDeviceOrientation:[UIDevice currentDevice].orientation]);
    NSLog(@"********************************************************************");
    NSLog(@"%@", [self descriptionForInterfaceOrientation:[UIApplication sharedApplication].statusBarOrientation]);
}

- (NSString *)descriptionForDeviceOrientation:(UIDeviceOrientation)deviceOrientation {
    switch (deviceOrientation) {
        case UIDeviceOrientationLandscapeLeft:
            return @"设备左向";
            break;
        case UIDeviceOrientationLandscapeRight:
            return @"设备右向";
            break;
        case UIDeviceOrientationPortraitUpsideDown:
            return @"设备倒向";
            break;
        case UIDeviceOrientationPortrait:
            return @"设备正向";
            break;
        default:
            break;
    }
    return @"其他方向";
    
}


- (NSString *)descriptionForInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
    switch (interfaceOrientation) {
        case UIInterfaceOrientationLandscapeLeft:
            return @"程序左向";
            break;
        case UIInterfaceOrientationLandscapeRight:
            return @"程序右向";
            break;
        case UIInterfaceOrientationPortraitUpsideDown:
            return @"程序倒向";
            break;
        case UIInterfaceOrientationPortrait:
            return @"程序正向";
            break;
        default:
            break;
    }
    return @"其他方向";
    
}




#pragma mark - <statusBarOrientationChanged>
- (void)statusBarOrientationChanged:(NSNotification *)noti {
    NSLog(@"#2 receive interface orientation change");
    NSLog(@"%@", [self descriptionForDeviceOrientation:[UIDevice currentDevice].orientation]);
    NSLog(@"%@", [self descriptionForInterfaceOrientation:[UIApplication sharedApplication].statusBarOrientation]);
}


/**
 *  横屏 UI 布局 与 竖屏的 UI 布局不同,如何来做?!
 *  在屏幕发生旋转的时候重新进行layout布局即可! , 调用 viewWillTransitionToSize:方法;
 */
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
    [coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext>  _Nonnull context) {
        //正在旋转中...
        
    } completion:^(id<UIViewControllerTransitionCoordinatorContext>  _Nonnull context) {
        //旋转完成!
        NSLog(@"screen bounds = %@" , NSStringFromCGRect([UIScreen mainScreen].bounds));
    }];
    [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
}


//屏幕旋转的过渡动画不够流畅 , 此时应该把 MainInterface 中的内 Main 删除即可!
- (void)viewDidLayoutSubviews {
    if (UIInterfaceOrientationIsPortrait([UIApplication sharedApplication].statusBarOrientation)) {
        _button.frame = CGRectMake(100, 100, 150, 50);
        _pushButton.frame = CGRectMake(100, 200, 150, 50);
        _textField.frame = CGRectMake(100, 300, 200, 30);
    } else {
        _button.frame = CGRectMake(100, 100, 150, 50);
        _pushButton.frame = CGRectMake(100, 200, 150, 50);
        _textField.frame = CGRectMake(300, 100, 200, 30);
    }
}


#pragma mark - 键盘变换通知
- (void)keyboardDidShow:(NSNotification *)noti {
    //NSLog(@"keyboard show, user info %@", notification.userInfo);
    NSLog(@"keyboard show user info %@" , noti.userInfo);
}


/**
 *  强制横屏方法用transform:view.transform = CGAffineTransformMakeRotation(M_PI_2);
 *  但是用transform方法旋转的话需要进行隐藏statusBar操作 , 因为statusBar不会根据屏幕跟随旋转!
 *  UIDevice.orientation方法:
 *  NSNumber *number = [NSNumber numberWithInteger:UIDeviceOrientationPortrait];
 [[UIDevice currentDevice] setValue:value forKey:@"orientation"];
 
 */

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    
}

@end

TestSecondViewController.h

#import <UIKit/UIKit.h>
@class TestSecondViewController;

@protocol TestSecondViewControllerDelegate <NSObject>

- (void)testSecondViewControllerDidDismiss:(TestSecondViewController *)testSecondViewController;

@end

@interface TestSecondViewController : UIViewController

@property (nonatomic, weak) id<TestSecondViewControllerDelegate> delegate;

- (instancetype)initWithFromPush:(BOOL)fromPush;

@end

TestSecondViewController.m

#import "TestSecondViewController.h"

@interface TestSecondViewController ()
{
    UIButton *_button;
    BOOL _fromPush;
}
@end

@implementation TestSecondViewController


#pragma mark - init
- (instancetype)initWithFromPush:(BOOL)fromPush {
    self = [super init];
    if (self) {
        //在构造方法里记录一下,传进来的这个布尔值:
        _fromPush = fromPush;
    }
    return self;
}


- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.view.backgroundColor = [UIColor lightGrayColor];
    _button = [[UIButton alloc] init];
    _button.backgroundColor = [UIColor redColor];
    _button.frame = CGRectMake(100, 100, 150, 50);
    
    if (_fromPush) {
        [_button setTitle:@"Pop" forState:UIControlStateNormal];
    } else {
        [_button setTitle:@"Dismiss" forState:UIControlStateNormal];
    }
    [_button addTarget:self action:@selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:_button];
    
}


#pragma mark - 按钮点击事件
- (void)buttonClicked:(id)sender {
    if (_fromPush) {
        [self.navigationController popViewControllerAnimated:YES];
    } else {
        if (_delegate && [_delegate respondsToSelector:@selector(testSecondViewControllerDidDismiss:)]) {
            [_delegate testSecondViewControllerDidDismiss:self];
        }
    }
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    
}

@end

PresentAnimation.h

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


@interface PresentAnimation : NSObject <UIViewControllerAnimatedTransitioning>

@end

PresentAnimation.m

#import "PresentAnimation.h"

@implementation PresentAnimation


#pragma mark - <UIViewControllerAnimatedTransitioning>
/**
 *  动画时长:
 */
- (NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext
{
    return 1.0f;
}

/**
 *  动画效果:
 */
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext
{
    //viewControllerKey:
    UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    //viewKey:
    UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey];
    
    UIView *containerView = [transitionContext containerView];
    [containerView addSubview:toView];
    CGRect targetFrame = [transitionContext finalFrameForViewController:toVC];
    //偏移量的操作:
    //targetFrame = CGRectOffset(targetFrame, 0, 500);
    toView.frame = CGRectOffset(targetFrame, 0, -targetFrame.size.height);
    NSTimeInterval duration = [self transitionDuration:transitionContext];
    [UIView animateWithDuration:duration delay:0 usingSpringWithDamping:0.6 initialSpringVelocity:0 options:UIViewAnimationOptionCurveEaseIn animations:^{
        
        toView.frame = targetFrame;
        
    } completion:^(BOOL finished) {
        //过场动画完成:
        [transitionContext completeTransition:YES];
        
    }];
    
}

@end

DismissAnimation.h

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

@interface DismissAnimation : NSObject <UIViewControllerAnimatedTransitioning>

@end

DismissAnimation.m

#import "DismissAnimation.h"

@implementation DismissAnimation

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

- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
    UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey];
    
    UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    UIView *fromView = [transitionContext viewForKey:UITransitionContextFromViewKey];
    
    UIView *containerView = [transitionContext containerView];
    [containerView addSubview:toView];
    [containerView sendSubviewToBack:toView];
    
    CGRect initFrame = [transitionContext initialFrameForViewController:fromVC];
    CGRect targetFrame = CGRectOffset(initFrame, 0, - initFrame.size.height);
    
    NSTimeInterval duration = [self transitionDuration:transitionContext];
    
    [UIView animateWithDuration:duration animations:^{
        
        fromView.frame = targetFrame;
        
    } completion:^(BOOL finished) {
        
        [transitionContext completeTransition:YES];
    }];
}

@end

CustomNavigationControllerDelegate.h

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

@interface CustomNavigationControllerDelegate : NSObject <UINavigationControllerDelegate>

@property (nonatomic, weak) UINavigationController *navigationController;

@end

CustomNavigationControllerDelegate.m

#import "CustomNavigationControllerDelegate.h"
#import "TransformAnimation.h"
#import "PresentAnimation.h"

@interface CustomNavigationControllerDelegate ()

{
    UIPercentDrivenInteractiveTransition *_innerPercentTransition;
    
    BOOL _underInteracting;
}

@end

@implementation CustomNavigationControllerDelegate

- (nullable id <UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
                                            animationControllerForOperation:(UINavigationControllerOperation)operation
                                                         fromViewController:(UIViewController *)fromVC
                                                           toViewController:(UIViewController *)toVC  NS_AVAILABLE_IOS(7_0)
{
    if (operation == UINavigationControllerOperationPop)
    {
        return [[TransformAnimation alloc] init];
    } else if (UINavigationControllerOperationPush)
    {
        return [[PresentAnimation alloc] init];
    }
    return nil;
}


#pragma mark - 交互式转场
- (nullable id <UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController
                                   interactionControllerForAnimationController:(id <UIViewControllerAnimatedTransitioning>) animationController
{
    if (_underInteracting)
    {
        return _innerPercentTransition;
    }
    
    return nil;
}

//setter方法:
- (void)setNavigationController:(UINavigationController *)navigationController
{
    _navigationController = navigationController;
    
    UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];
    [_navigationController.view addGestureRecognizer:panGesture];
}


#pragma mark - 手势
- (void)pan:(id)sender
{
    if ([sender isKindOfClass:[UIPanGestureRecognizer class]])
    {
        UIPanGestureRecognizer *panGesture = sender;
        
        switch (panGesture.state) {
            case UIGestureRecognizerStateBegan:
            {
                _underInteracting = YES;
                
                _innerPercentTransition = [[UIPercentDrivenInteractiveTransition alloc] init];
                
                if ([_navigationController.viewControllers count] > 1)
                {
                    [_navigationController popViewControllerAnimated:YES];
                }
                break;
            }
                
            case UIGestureRecognizerStateChanged:
            {
                CGPoint translationPoint = [panGesture translationInView:_navigationController.view];
                CGFloat progress = translationPoint.x / CGRectGetWidth(_navigationController.view.bounds);
                
                [_innerPercentTransition updateInteractiveTransition:progress];
                
                
                break;
            }
            case UIGestureRecognizerStateEnded:
            {
                _underInteracting = NO;
                
                if ([panGesture velocityInView:_navigationController.view].x > 0) {
                    [_innerPercentTransition finishInteractiveTransition];
                }
                else
                {
                    [_innerPercentTransition cancelInteractiveTransition];
                }
                _innerPercentTransition = nil;
                break;
            }
                
            default:
                [_innerPercentTransition cancelInteractiveTransition];
                _innerPercentTransition = nil;
                break;
        }
    }
}

@end

TransformAnimation.h

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

@interface TransformAnimation : NSObject <UIViewControllerAnimatedTransitioning>

@end

TransformAnimation.m

#import "TransformAnimation.h"

@implementation TransformAnimation

- (NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext
{
    return 1.0f;
}

- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext
{
    UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    [[transitionContext containerView] addSubview:toVC.view];
    toVC.view.alpha = 0;
    [UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
        fromVC.view.transform = CGAffineTransformMakeScale(0.1, 0.1);
        toVC.view.alpha = 1;
    } completion:^(BOOL finished) {
        //还原fromVC:
        fromVC.view.transform = CGAffineTransformIdentity;
        //动画结束后,完成上下文:
        [transitionContext completeTransition:YES]; 
    }];
}

@end

愿编程让这个世界更美好

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容