一. 简介

在iOS中并没有专门的模态窗口类,模态窗口(modal)只是视图控制器显示的一种方式,模态窗口方便快捷,它不依赖于控制器容器(如UITabBarController和UINavigationController),通常用于显示独立的内容,在模态窗口显示的时候,它暂时中断程序正常的执行流程,用户无法再与上一个场景交互,只能对当前此窗口进行操作,除非他们先关闭这个场景,而普通窗口则可以进行任意切换

推荐一篇可以加深对模态理解的博文: http://blog.csdn.net/ssirreplaceable/article/details/52335465

1. 使用场景

模态视图(modal)最常用的场景是临时弹出的窗口,如登陆注册、设置、购物车、点赞等展示小功能的窗口
使用场景可分为下面几类:

a. 收集用户输入信息

b. 临时呈现一些内容

c. 临时改变工作模式

d. 为不同的设备方向实现可替代的界面

e. 使用指定类型的过渡动画来显示一个新的视图结构

2. 基本概念

模态:一种用来承载特定内容、功能或体验的模式,有其自身的优缺点。它可以帮助用户完成某些任务,或在不受干扰的情况下获取信息,但会暂时性的强迫用户停止与应用其他部分的互动

模态视图:也称为模态窗口,由于在iOS中并没有专门的模态窗口类,模态窗口(modal)在iOS中只是视图控制器显示的一种方式,通过presentViewController方法弹出的视图我们都称为模态视图

二. 知识点

  1. 模态视图没有父控制器,即当视图通过parentViewController获取父视图时,返回nil

  2. 当我们在viewController A中模态显示viewController B的时候,A就充当presentingViewController(弹出模态窗口的控制器),而B就是presentedViewController(被弹出控制器)

  3. 当连续获取presentingViewController,可以得到根视图控制器

// 当连续两次展现模态视图时,可以通过下面代码获取根视图控制器,self指向最后展现的模态窗口
UIViewController *vc = self.presentingViewController.presentingViewController;

三. 展现和关闭模态视图方法

present方法和dismiss方法一般一起使用,才能实现两个视图之间的来回切换
completion是一个block类型的处理器,用于在调用present方法或dismiss方法之后执行回调

// 展示模态视图
- (void)presentViewController:(UIViewController *)viewControllerToPresent animated: (BOOL)flag completion:(void (^)(void))completion NS_AVAILABLE_IOS(5_0);
// 关闭模态视图
- (void)dismissViewControllerAnimated: (BOOL)flag completion: (void (^)(void))completion NS_AVAILABLE_IOS(5_0);

四. 模态窗口的切换风格

必须在presenting控制器里设置
(1). Modal Presentation Styles(弹出风格)

typedef NS_ENUM(NSInteger, UIModalPresentationStyle) {
        UIModalPresentationFullScreen = 0,
        UIModalPresentationPageSheet NS_ENUM_AVAILABLE_IOS(3_2),
        UIModalPresentationFormSheet NS_ENUM_AVAILABLE_IOS(3_2),
        UIModalPresentationCurrentContext NS_ENUM_AVAILABLE_IOS(3_2),
        UIModalPresentationCustom NS_ENUM_AVAILABLE_IOS(7_0),
        UIModalPresentationOverFullScreen NS_ENUM_AVAILABLE_IOS(8_0),
        UIModalPresentationOverCurrentContext NS_ENUM_AVAILABLE_IOS(8_0),
        UIModalPresentationPopover NS_ENUM_AVAILABLE_IOS(8_0),
        UIModalPresentationNone NS_ENUM_AVAILABLE_IOS(7_0) = -1,         
};

(2). Modal Transition Style(弹出时的动画风格)

typedef NS_ENUM(NSInteger, UIModalTransitionStyle) {
    UIModalTransitionStyleCoverVertical = 0,
    UIModalTransitionStyleFlipHorizontal,
    UIModalTransitionStyleCrossDissolve,
    UIModalTransitionStylePartialCurl NS_ENUM_AVAILABLE_IOS(3_2),
};

五. 窗口之间的传值

  1. 接收方必须要有一个接收数据的属性
  2. 可以通过presentingViewController获取弹出模态视图的那个控制器,而通过presentedViewController可获取被弹出的控制器视图
  3. 从前往后传值,要先调用presentViewController:animated:completion:方法才能进行传值,不能在调用之前传值,否者无法传值
  4. 从后往前传值,要先传值再调用dismissViewControllerAnimated:completion:方法,不能在调用之后传值,否者无法传值
  5. 官方文档建议这两者之间通过delegate实现交互

补充:之后会补充关于界面传值的文章

四. 具体使用

(一). storyboard中的使用

在storyboard里使用模态视图,即切换模式为modal,只能通过代码返回

1. 在视图控制器1选中“按钮”按住Ctrl键,拖线到视图控制器2,并选中modal切换模式

这里写图片描述


这里写图片描述

2. 设置视图控制器2的所属类

这里写图片描述

3. 编写返回按钮的响应方法,即通过代码关闭模态视图

#import "TwoViewController.h"

@interface TwoViewController ()

@end

@implementation TwoViewController

- (IBAction)gotoOne:(id)sender {

  [self dismissViewControllerAnimated: YES completion:nil];

}

@end

(二). 纯代码实现

1. ViewController类

----------  ViewController.h文件

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController

@property (nonatomic,strong) NSString *oneData;

@end

---------- ViewController.m文件

#import "ViewController.h"
#import "TwoViewController.h"

@interface ViewController ()

@property (nonatomic,strong) UIButton *gotoTwoBtn;

@end

@implementation ViewController

- (void)viewDidLoad {
  [super viewDidLoad];

  // 设置背景颜色
  self.view.backgroundColor = [UIColor blueColor];

  // 创建模态切换按钮
  _gotoTwoBtn = [[UIButton alloc] init];
  _gotoTwoBtn.frame = CGRectMake(100, 150, 80, 60);
  [_gotoTwoBtn setBackgroundImage:[UIImage imageNamed:@"beijing"] forState:UIControlStateNormal];
  [_gotoTwoBtn addTarget:self action:@selector(gotoTwo) forControlEvents:UIControlEventTouchUpInside];
  [self.view addSubview:_gotoTwoBtn];

}

- (void)gotoTwo{

  TwoViewController *twoVC = [[TwoViewController alloc] init];

  [self presentViewController:twoVC animated:YES completion:nil];

  // 要先执行presentViewController:animated:completion:才能执行下面语句
  [self.presentedViewController setValue:@"界面1到界面2" forKey:@"twoData"];

}

- (void)viewDidAppear:(BOOL)animated {

  NSLog(@"%@ -- %@",self,_oneData);

}

@end

2. TwoViewController类

---------- TwoViewController.h文件

#import <UIKit/UIKit.h>

@interface TwoViewController : UIViewController

@property (nonatomic,strong) NSString *twoData;

@end

---------- TwoViewController.m文件

#import "TwoViewController.h"
#import "TreeViewController.h"

@interface TwoViewController ()

@property (nonatomic,strong) UIButton *backOneBtn;

@property (nonatomic,strong) UIButton *gotoTreeBtn;

@end

@implementation TwoViewController

- (void)viewDidLoad {
  [super viewDidLoad];

  // 设置视图背景颜色
  self.view.backgroundColor = [UIColor greenColor];

  // 创建模态关闭按钮
  _backOneBtn = [[UIButton alloc] init];
  _backOneBtn.frame = CGRectMake(100, 150, 80, 60);
  [_backOneBtn setBackgroundImage:[UIImage imageNamed:@"beijing"] forState:UIControlStateNormal];
  [_backOneBtn addTarget:self action:@selector(gotoOne) forControlEvents:UIControlEventTouchUpInside];
  [self.view addSubview:_backOneBtn];

  // 创建模态切换按钮
  _gotoTreeBtn = [[UIButton alloc] init];
  _gotoTreeBtn.frame = CGRectMake(100, 250, 80, 60);
  [_gotoTreeBtn setBackgroundImage:[UIImage imageNamed:@"beijing"] forState:UIControlStateNormal];
  [_gotoTreeBtn addTarget:self action:@selector(gotoTree) forControlEvents:UIControlEventTouchUpInside];
  [self.view addSubview:_gotoTreeBtn];

}

- (void)gotoOne{
 [self.presentingViewController setValue:@"界面2到界面1" forKey:@"oneData"];

  [self dismissViewControllerAnimated:YES completion:nil];

}

- (void)gotoTree{

  TreeViewController *treeVC = [[TreeViewController alloc] init];

  [self presentViewController:treeVC animated:YES completion:nil];

  [self.presentedViewController setValue:@"界面2到界面3" forKey:@"treeData"];
}

- (void)viewWillAppear:(BOOL)animated {

  NSLog(@"%@ -- %@",self,_twoData);

}

@end

3. TreeViewController类

---------- TreeViewController.h文件

#import <UIKit/UIKit.h>

@interface TreeViewController : UIViewController

@property (nonatomic,strong) NSString *treeData;

@end

---------- TreeViewController.m文件

#import "TreeViewController.h"

@interface TreeViewController ()

@property (nonatomic,strong) UIButton *backTwoBtn;

@property (nonatomic,strong) UIButton *backOneBtn;

@end

@implementation TreeViewController

- (void)viewDidLoad {
    [super viewDidLoad];

  // 设置视图背景颜色
  self.view.backgroundColor = [UIColor redColor];

  // 创建模态关闭按钮
  _backTwoBtn = [[UIButton alloc] init];
  _backTwoBtn.frame = CGRectMake(100, 150, 80, 60);
  [_backTwoBtn setBackgroundImage:[UIImage imageNamed:@"beijing"] forState:UIControlStateNormal];
  [_backTwoBtn addTarget:self action:@selector(gotoTwo) forControlEvents:UIControlEventTouchUpInside];
  [self.view addSubview:_backTwoBtn];

  // 创建模态切换按钮
  _backOneBtn = [[UIButton alloc] init];
  _backOneBtn.frame = CGRectMake(100, 250, 80, 60);
  [_backOneBtn setBackgroundImage:[UIImage imageNamed:@"beijing"] forState:UIControlStateNormal];
  [_backOneBtn addTarget:self action:@selector(gotoOne) forControlEvents:UIControlEventTouchUpInside];
  [self.view addSubview:_backOneBtn];

}

- (void)gotoTwo{

  [self.presentingViewController setValue:@"界面3到界面2" forKey:@"twoData"];

  [self dismissViewControllerAnimated:YES completion:nil];

}

- (void)gotoOne{

  // 连续presentingViewController可以获取根控制器
  UIViewController *vc = self.presentingViewController.presentingViewController;

  [vc setValue:@"界面3到界面1" forKey:@"oneData"];

  [vc dismissViewControllerAnimated:YES completion:nil];

}

- (void)viewDidAppear:(BOOL)animated {

  NSLog(@"%@ -- %@",self,_treeData);

}

@end
Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐