iOS-UI 进阶

1,029 阅读9分钟

UIApplication

- (void) testUIApplication {
    // 单例模式实现
    UIApplication *app = [UIApplication shareApplication];
    
    // 设置icon通知数字
    // - ios9以上需要设置通知
    UIUserNotificationCategory *category = [[UIUserNotificationCategory alloc] init];
    NSSet *set = [NSSet setWithObject:category];
    UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsTypes:UIUserNotificationTypeBadge categore:set];
    [app registerUserNotificationSettings:setting];
    // 设置数字, 0不显示
    app.applicationIconBadgeNumber = 10;
    
    // 设置联网状态指示器
    app.networkActivityIndicatorVisible = YES;
    
    // 打电话/发短信/邮件/跳转到其他应用界面
    // [app openURL:];
    
    // 状态栏管理 
    // target的info.plist需要设置 [View controller-based status bar appearance] = NO
    // 由controller控制
    app.statusBarHidden = YES; // 隐藏状态栏
    app.statusBarStyle = UIStatusBarStyleLightContent; // 设置状态栏高亮模式
     
}

-(BOOL) prefersStatusBarHidden {
    // [View controller-based status bar appearance] = YES, 此方法才生效
    return YES;
}

应用启动过程

应用启动过程:
    1. 入口 main.m main 函数
    2. 创建自动释放池
    3. 执行 UIApplicationMain 无限循环运行
    4. 第三个参数 nil : 相当于 @"UIApplication" 创建一个应用程序对象
    5. 应用代理对象
    6. 将应用代理对象内的 UIWindow 实例化, 并设置为应用程序的 keyWindow
    7. 加载配置文件中指定的storyboard[Main.storyboard]文件中带箭头的控制器

@implementation AppDelegate
    - (BOOL) application:(UIApplication *) application didFinishLaunchingWithOPtions:(NSDictionary *) launchOptions {
        // 应用加载完毕
        return YES;
    }
    
    -(void) applicationWillResignActive:(UIApplication *) application {
        // 即将变为不活跃状态 [失去焦点]
    }
    
    -(void) applicationDidEnterBackground:(UIApplication *) application {
        // 应用进入后台
        // 在这里保存状态和数据
    }
    -(void) applicationWillEnterForeground:(UIApplication *) application {
        // 应用进入前台
    }
    
    -(void) applicationDidBecomeActive:(UIApplication *) application {
        // 即将变为活跃状态 [获得焦点]
    }
    
    - (void) applicationWillTerminate:(UIApplication *) application {
        // 应用被销毁
        // 不一定会执行,按住home到后台,或一段时间划掉应用,不会执行
    }
@end

手动创建 UIWindow

UIWindow *win = [[UIWindow alloc] initWithFrame:CGRectMake(20, 20, 200, 200)];
win.backgroundColor = [UIColor redColor];
win.hidden = NO; // UIWinow默认隐藏,必须设置为不隐藏才会显示出来

[self.view addSubview:win];

// 兼容ios版本
// 在第三方框架中显示UIWindow
[[[UIApplication sharedApplication].windows lastObject] addSubview:win];

// 在ios13及以上系统,保留SceneDelegate,需要修改SceneDelegate里面的代码即可
- (void)scene:(UIScens *)scene willConnectToSession:(UISeccion *)session options:(UISceneConnecionOptions *)connectionOptions {
    if(@available(ios 13, *) && scene) {
        self.window = [[UIWindow alloc] initWithWindowScene:(UIWindowScene *)scene];
        self.window.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height);

        UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
        ViewController *viewController = [mainStoryboard instantiateViewControllerWithIdentifier:@"index"];

        UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:viewController];
        self.window.rootViewController = nav;
        [self.window makeKeyAndVisible];
    }
}

// 针对iOS13系统以下:
//    a. 删除info.plist文件中的Application Scene Manifest选项;
//    b. 删除SceneDelegate文件;
//    c. 删除AppDelegate里面的UISceneSession lifecycle方法;
//    d. AppDelegate头文件添加window属性;
        @property (strong, nonatomic) UIWindow *window;
//    e. 修改AppDelegate启动方法:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
    ViewController *vc = [[ViewController alloc]init];
    UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:vc];
    self.window.rootViewController = nav;
    [self.window makeKeyAndVisible];
    return YES;
}

加载自定义ViewController (代码、storyboard、xib)

// 控制器的View是懒加载的,当时初始化后或者使用self.view 时才会创建

#import <UIKit/UIKit.h>
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@end

// 1. 通过代码创建ViewController
@interface AppDelegate ()
@end
@implementation AppDelegate
    - (BOOL) application:(UIApplication *) application didFinishLaunchingWithOPtions:(NSDictionary *) launchOptions {
        // 1. 创建窗口
        self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bound];
        
        // 2. 设置窗口控制器
        // 2.1 创建控制器
        CTViewController *viewController = [[CTViewController alloc] init];
        // 2.2 设置窗口的根控制器
        self.window.rootViewController = viewController;
        
        // 3. 将窗口设置为应用程序的主窗口, 并设置为可见[VIsible]
        [self.window makeKeyAndVisible];
        // [self.window makeKeyWindow];
        // self.window.hidden = NO;
        return YES;
    }
@end


// 2. 通过storyboard创建ViewController
@interface AppDelegate ()
@end
@implementation AppDelegate
    - (BOOL) application:(UIApplication *) application didFinishLaunchingWithOPtions:(NSDictionary *) launchOptions {
        // 1. 创建窗口
        self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bound];
        
        // 2. 设置窗口控制器
        // 2.1.1 创建 storyboard 文件,关联控制器为 CTViewController
        // 2.1.2 加载 storyboard 文件
        UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"CTStroryboard" bundle:nil];
        // 2.1.3 从storyboard实例控制器
        // storyboard可以拥有多个控制器,默认加载 最后一个
        // UIViewController *viewController = [storyboard instantiateInitialViewController];
        // 在storyboard中配置控制器的Identifier, 加载指定控制
        UIViewController *viewController = [storyboard instantiateViewControllerWithIdentifier:@"login"];
        // 2.2 设置窗口的根控制器
        self.rootViewController = viewController;
        
        // 3. 将窗口设置为应用程序的主窗口, 并设置为可见[VIsible]
        [self.window makeKeyAndVisible];
        // [self.window makeKeyWindow];
        // self.window.hidden = NO;
        return YES;
    }
@end


// 3. 通过storyboard创建ViewController
@interface AppDelegate ()
@end
@implementation AppDelegate
    - (BOOL) application:(UIApplication *) application didFinishLaunchingWithOPtions:(NSDictionary *) launchOptions {
        // 1. 创建窗口
        self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bound];
        
        // 2. 设置窗口控制器 
        // 2.1.1 创建 xxx.xib 文件,修改xib的 fileOwner 为 CTViewController,将fileOwner的view拖线至 xib 内的view
        
        // 没有指定xib名字,系统优先去找完全匹配的xib (CTViewController.xib),再找相识名字的xib (CTView.xib)
        // ViewController *viewController = [[CTViewController alloc] init];
        
        // 从指定xib文件名来创建viewController
        ViewController *viewController = [[CTViewController alloc] initWithNibName:@"xxx" bundle:nil];
        
        // 2.2 设置窗口的根控制器
        self.rootViewController = viewController;
        
        // 3. 将窗口设置为应用程序的主窗口, 并设置为可见[VIsible]
        [self.window makeKeyAndVisible];
        // [self.window makeKeyWindow];
        // self.window.hidden = NO;
        return YES;
    }
@end

导航栏控制器 UINavigationController 控制多个界面切换

1. 创建导航栏控制器
    UINavigationController *nav = [[UINavigationController alloc] init];
    // 创建并指定主controllerView
    UINavigationController *nav = [[UINavigationController alloc] initWithRootViewcontroller:[[ViewController alloc] init]];
2. 显示下一个控制器
    [viewController.navigationController pushViewController:[[ViewController alloc] init] animated:YES];
3. 返回上一个控制器
    [viewController.navigationController popToRootViewControllerAnimated:YES];
4. 返回指定控制器,只能返回在栈中的实例 viewController.navigationController.viewControllers
    [viewController.navigationController popToViewController:viewController animated:YES];

5. 配置导航栏
    // 控制器导航栏 显示标题
    viewController.navigationItem.titleView;
    // 控制器返回按钮 UIBarButtonItem,在下一个界面实现的返回按钮
    viewController.navigationItem.backBarButtonItem;
    // 当前界面导航栏左边按钮
    viewController.navigationItem.leftBarButtonItem;
    // 当前界面导航栏左边多个按钮
    viewController.navigationItem.leftBarButtonItems;
    // 当前界面导航栏右边多个按钮
    viewController.navigationItem.rightBarButtonItems;

UITabBarController (底部tab按钮 + 控制多个界面切换)

#import "SceneDelegate.h"

@interface SceneDelegate ()
@end

@implementation SceneDelegate
    - (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions {
        self.window = [[UIWindow alloc] initWithWindowScene:(UIWindowScene *)scene];
        self.window.frame = [UIScreen mainScreen].bounds;

        UITabBarController *tabBarController = [[UITabBarController alloc] init];
        self.window.rootViewController = tabBarController;

        UIViewController *vc1 = [[UIViewController alloc] init];
        vc1.view.backgroundColor = [UIColor redColor];
        vc1.tabBarItem.title = @"红色界面";
    //    vc1.tabBarItem.imagge = [UIImage imageNamed:@"xxx"];
        vc1.tabBarItem.badgeValue = @"99"; // 按钮右上角显示

        UIViewController *vc2 = [[UIViewController alloc] init];
        vc2.view.backgroundColor = [UIColor blueColor];
        vc2.tabBarItem.title = @"蓝色界面";
    //    vc1.tabBarItem.imagge = [UIImage imageNamed:@"xxx"];
        vc2.tabBarItem.badgeValue = @"蓝色"; // 按钮右上角显示

        [tabBarController addChildViewController:vc1];
        [tabBarController addChildViewController:vc2];

        [self.window makeKeyAndVisible];
    }
@end

控制器生命周期

#import "ViewController.h"

@interface ViewController ()
@end

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    // 界面加载完成
}
- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    // 界面即将显示
}
- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    // 界面已经显示出来
}
- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    // 界面即将消失
}
- (void)viewDidDisappear:(BOOL)animated {
    [super viewDidDisappear:animated];
    // 界面已经消失
}
- (void)dealloc {
    // viewControll 销毁
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    // controllerView 传值
}
@end

// A、B、C 控制器依次显示,当C返回A时,B、C依次销毁调用 dealloc 。

// 快速修改storyboard的默认controllerView为navigationController
// 视图中选中controller -> Editor -> Embed in -> Navigation Controller

保存文件到沙盒中, 模型归档解档

// 文本输入框成为第一响应者
[self.numberField becomeFirstResponder];

// 沙盒目录 沙盒目录不固定, 由系统管理
NSString *homePaht = NSHomeDirectory();
NSString *docPath = [homePath stringByAppendingString:@"/Documents"];
NSString *docPath = [homePath stringByAppendingPathComponent:@"Documents"];

// 使用搜索文件夹 找到 document
NSString *docPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];

// 保存文件
NSString *filePath = [docPath stringByAppendingPathComponent:@"xx.plist"];
NSArray *array = @["111", "222", "德玛西亚"];
[array writeToFile:filePath atomically:YES];
// 读取文件
array = [NSArray arrayWithContentOfFile:filePath];

// 偏好设置
NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults];
// 添加
[preferences setObject:@"value" forKey:@"key"];
[preferences setBool:YES forKey:@"boolKey"];
// [preferences synchronize]; // 立即保存, ios8后自动调用不需要手动调用
// 获取
NSString *value = [preferences objectForKey:@"key"];
BOOL boolKey = [preferences boolForKey:@"boolKey"];


// 归档解档 Object<NSSecureCoding> <-> NSDate
@interface Person <NSSecureCoding>
    @property(nonatamic, copy) NSString *name;
    @property(nonatamic, assign) int *age;
@end
@implemention Persion
    - (instancetype)initWithCoder:(NSCoder *)coder {
        _name = [coder decodeObjectForKey:@"name"];
        _age = [coder decodeIntForKey:@"age"];
    }
    - (void)encodeWithCoder:(NSCoder)coder {
        [coder encodeObject:self.name forKey:@"name"];
        [coder encodeInt:self.age forKey:@"age"];
    }
@end

void main() {
    Person *person = [[Person alloc] init];
    person.name = @"xxx";
    person.age = 18;
    NSString *docPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
    NSString *personDataFilePath = [docPath stringByAppendingPathComponent:@"person.data"];
    // 归档
    [NSKeyedArchiver archiveRootObject:person toFile:personDataFilePath];
    // 解档
    Person *newPerson = [NSKeyedUnarchiver unarchiveObject:personDataFilePath];
}

通讯录源码 (TabBar嵌套Navigation)

// IFContactModel.h
@interface IFContactModel : NSObject <NSSecureCoding>
    @property (nonatomic, copy) NSString *username;
    @property (nonatomic, copy) NSString *number;
@end
// IFContactModel.m
#import "IFContactModel.h"
@implementation IFContactModel
    - (instancetype)initWithCoder:(NSCoder *)coder {
        if (self = [super init]) {
            _username = [coder decodeObjectForKey:@"username"];
            _number = [coder decodeObjectForKey:@"number"];
        }
        return self;
    }
    - (void)encodeWithCoder:(NSCoder *)coder {
        [coder encodeObject:self.username forKey:@"username"];
        [coder encodeObject:self.number forKey:@"number"];
    }
    + (BOOL)supportsSecureCoding {
       return YES;
    }
@end



// LoginViewController.h
@interface LoginViewController : UIViewController
@end
// LoginViewController.m
#import "LoginViewController.h"
#import "JGProgressHUD.h"

#define kUsername @"kUsername"
#define kPassword @"kPassword"
#define kRememberPassowrd @"kRememberPassowrd"
#define kAutoLogin @"kAutoLogin"

#define preferences [NSUserDefaults standardUserDefaults]

#define isEmpty(str) str == nil || [str isEqualToString:@""]
#define isNotEmpty(str) str != nil && ![str isEqualToString:@""]

@interface LoginViewController ()
    @property (weak, nonatomic) IBOutlet UITextField *username;
    @property (weak, nonatomic) IBOutlet UITextField *password;
    @property (weak, nonatomic) IBOutlet UISwitch *rememberPassword;
    @property (weak, nonatomic) IBOutlet UISwitch *autoLogin;
    @property (weak, nonatomic) IBOutlet UIButton *btnLogin;
@end

@implementation LoginViewController
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view.
        [self.username addTarget:self action:@selector(textChange) forControlEvents:UIControlEventEditingChanged];
        [self.password addTarget:self action:@selector(textChange) forControlEvents:UIControlEventEditingChanged];
        self.btnLogin.enabled = NO;

        self.autoLogin.on = [preferences boolForKey:kAutoLogin];
        self.rememberPassword.on = [preferences boolForKey:kRememberPassowrd];

        if ([preferences boolForKey:kRememberPassowrd]) {
            if (isNotEmpty([preferences objectForKey:kUsername])) {
                self.username.text = [preferences objectForKey:kUsername];
            }
            if (isNotEmpty([preferences objectForKey:kPassword])) {
                self.password.text = [preferences objectForKey:kPassword];
            }
            [self textChange];
        }

        if ([preferences boolForKey:kAutoLogin]) {
            [self login:self.btnLogin];
        } else {
            if (isEmpty(self.username.text)) {
                [self.username becomeFirstResponder];
            } else if (isEmpty(self.password.text)) {
                [self.password becomeFirstResponder];
            }
        }
    }

    - (void) textChange {
        // 当输入框内容改变
        self.btnLogin.enabled = (self.username.text.length > 0 && self.password.text.length > 0) ? YES : NO;
    }

    - (IBAction)rememberPasswordChange:(UISwitch *)sender {
        if (!sender.isOn) {
            [self.autoLogin setOn:NO animated:YES];
        }
        [preferences setBool:self.rememberPassword.isOn forKey:kRememberPassowrd];
        [preferences setBool:self.autoLogin.isOn forKey:kAutoLogin];
    }

    - (IBAction)autoLoginChange:(UISwitch *)sender {
        if (sender.isOn) {
            [self.rememberPassword setOn:YES animated:YES];
        }
        [preferences setBool:self.rememberPassword.isOn forKey:kRememberPassowrd];
        [preferences setBool:self.autoLogin.isOn forKey:kAutoLogin];
    }

    - (IBAction)login:(UIButton *)sender {
        __block JGProgressHUD *HUD = [[JGProgressHUD alloc]  initWithStyle:JGProgressHUDStyleDark];
        HUD.textLabel.text = @"Loading";
        [HUD showInView:self.view];

        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            [HUD dismissAnimated:YES];
            if ([self.username.text isEqualToString:@"1"] && [self.password.text isEqualToString:@"1"]) {
                // 手动跳转
                [self performSegueWithIdentifier:@"login2account" sender:nil];

                if ([preferences boolForKey:kRememberPassowrd]) {
                    [preferences setObject:self.username.text forKey:kUsername];
                    [preferences setObject:self.password.text forKey:kPassword];
                }
            } else {
                // 提示账号或密码错误
                JGProgressHUD *errorView = [[JGProgressHUD alloc] initWithStyle:JGProgressHUDStyleDark];
                errorView.textLabel.text = @"账号或密码错误";
                errorView.indicatorView = [[JGProgressHUDErrorIndicatorView alloc] init];
                [errorView showInView:self.view];
                [errorView dismissAfterDelay:1.0];
            }
        });
    }
@end


// ContactTableViewController.h
#import <UIKit/UIKit.h>
@interface ContactTableViewController : UITableViewController
@end
// ContactTableViewController.m
#import "ContactTableViewController.h"
#import "AddContactViewController.h"
#import "EditContactViewController.h"
#import "IFContactModel.h"

#define kContactsPath [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0] stringByAppendingPathComponent:@"contacts.data"]

@interface ContactTableViewController () <AddContactDelegate, EditContactDelegate>
    @property (nonatomic, strong) NSMutableArray *contacts;
@end

@implementation ContactTableViewController
    - (void)viewDidLoad {
        [super viewDidLoad];

        self.contacts = [[NSMutableArray alloc] init];

        // 读取通讯录数据
        NSSet *set = [NSSet setWithArray:@[[NSArray class], [IFContactModel class]]];
        NSData *data = [NSData dataWithContentsOfFile:kContactsPath];
        NSArray *tempArray = [NSKeyedUnarchiver unarchivedObjectOfClasses:set fromData:data error:nil];
        [self.contacts addObjectsFromArray:tempArray];
    }

    #pragma mark - Table view data source

    - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
        return 1;
    }

    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
        return self.contacts.count;
    }

    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"contact" forIndexPath:indexPath];

        IFContactModel *model = self.contacts[indexPath.row];

        cell.textLabel.text = model.username;
        cell.detailTextLabel.text = model.number;

        return cell;
    }

    // 重写此方法开启侧滑删除功能, 点击删除时事件
    - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
        [self.contacts removeObjectAtIndex:indexPath.row];

        [self cacheContacts];

        // 刷新数据
        // [self.tableView reloadData];
        [self.tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationLeft];
    }

    #pragma mark - Navigation
    - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
        if ([segue.destinationViewController isKindOfClass:[AddContactViewController class]]) {
            AddContactViewController * controller = (AddContactViewController *) segue.destinationViewController;
            controller.delegate = self;
        } else if ([segue.destinationViewController isKindOfClass:[EditContactViewController class]]) {
            EditContactViewController * controller = (EditContactViewController *) segue.destinationViewController;
            controller.delegate = self;
            controller.contact = self.contacts[self.tableView.indexPathForSelectedRow.row];
        }
    }

    - (IBAction)logout:(UIBarButtonItem *)sender {
        // UIAlertControllerStyleAlert 弹框, UIAlertControllerStyleActionSheet 底部弹框
        UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"是否确认注销?" message:@"注销后将返回登录" preferredStyle:UIAlertControllerStyleActionSheet];
        // 2.创建并添加按钮
        UIAlertAction *logoutAction = [UIAlertAction actionWithTitle:@"注销" style:UIAlertActionStyleDestructive handler:^(UIAlertAction * _Nonnull action) {
            [self.navigationController popViewControllerAnimated:YES];
        }];
        [alert addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil]];
        [alert addAction:logoutAction];
        [self presentViewController:alert animated:YES completion:^{}];
    }

    #pragma mark - AddContactDelegate
    - (void) addContactWithController:(AddContactViewController *) controller andIFContact:(IFContactModel *) contactModel {
        [self.contacts addObject:contactModel];

        // 保存通讯录数据
        [self cacheContacts];

        [self.tableView reloadData];
    }

    #pragma mark - EditContactDelegate
    - (void) editContactWithEditContactViewController:(EditContactViewController *) controller andContact:(IFContactModel *) contactModel {
        IFContactModel *con = self.contacts[self.tableView.indexPathForSelectedRow.row];
        con.username = contactModel.username;
        con.number = contactModel.number;
        [self.tableView reloadData];

        // 保存通讯录数据
        [self cacheContacts];
    }

    - (void)cacheContacts {
        // 保存通讯录数据
        NSData *data = [NSKeyedArchiver archivedDataWithRootObject:self.contacts requiringSecureCoding:NO error:nil];
        BOOL result = [data writeToFile:kContactsPath atomically:YES]; // atomically缓存写入,避免文件损坏
        NSLog(@"%s , result = %d", __func__, result);
    }

@end



// AddContactViewController.h
#import <UIKit/UIKit.h>
#import "IFContactModel.h"
@class AddContactViewController;
@protocol AddContactDelegate <NSObject>
    @optional
    - (void) addContactWithController:(AddContactViewController *) controller andIFContact:(IFContactModel *) contactModel;
@end
@interface AddContactViewController : UIViewController
    @property (nullable, nonatomic, weak) id<AddContactDelegate> delegate;
@end

// AddContactViewController.m
#import "AddContactViewController.h"
@interface AddContactViewController ()
    @property (weak, nonatomic) IBOutlet UITextField *username;
    @property (weak, nonatomic) IBOutlet UITextField *number;
    @property (weak, nonatomic) IBOutlet UIButton *btnSave;
@end
@implementation AddContactViewController
    - (void)viewDidLoad {
        [super viewDidLoad];

        [self.username becomeFirstResponder];
    }

    - (IBAction)textChange:(UITextField *)sender {
        self.btnSave.hidden = (self.username.text.length > 0 && self.number.text.length > 0) ? NO : YES;
    }

    - (IBAction)saveContact:(UIButton *)sender {
        IFContactModel *con = [[IFContactModel alloc] init];
        con.username = self.username.text;
        con.number = self.number.text;

        [self.delegate addContactWithController:self andIFContact:con];
        [self.navigationController popViewControllerAnimated:YES];
    }
@end



// EditContactViewController.h
#import <UIKit/UIKit.h>
#import "IFContactModel.h"
@class EditContactViewController;
@protocol EditContactDelegate <NSObject>
    @optional
    - (void) editContactWithEditContactViewController:(EditContactViewController *) controller andContact:(IFContactModel *) contactModel;
@end
@interface EditContactViewController : UIViewController
    @property(nonatomic, strong) IFContactModel *contact;
    @property(nonatomic, weak) id<EditContactDelegate> delegate;
@end

// EditContactViewController.m
#import "EditContactViewController.h"
@interface EditContactViewController ()
    @property (weak, nonatomic) IBOutlet UIButton *btnSave;
    @property (weak, nonatomic) IBOutlet UITextField *username;
    @property (weak, nonatomic) IBOutlet UITextField *number;
@end
@implementation EditContactViewController
    - (void)viewDidLoad {
        [super viewDidLoad];

        self.username.text = self.contact.username;
        self.number.text = self.contact.number;
    }

    - (IBAction)editClick:(UIBarButtonItem *)sender {
        BOOL canEdit = !self.username.enabled;
        self.btnSave.hidden = !canEdit;
        self.username.enabled = canEdit;
        self.number.enabled = canEdit;
        [sender setTitle:canEdit ? @"取消" : @"编辑"];
        if (canEdit) {
            [self.number becomeFirstResponder];
        }
    }

    - (IBAction)saveContact:(id)sender {
        // 同一个对象, 这里修改对数据源生效
    //    self.contact.username = self.username.text;
    //    self.contact.number = self.number.text;

        // 通过代理传递参数
        IFContactModel * con = [[IFContactModel alloc] init];
        con.username = self.username.text;
        con.number = self.number.text;
        [self.delegate editContactWithEditContactViewController:self andContact:con];

        [self.navigationController popViewControllerAnimated:YES];
    }
@end

image.png

UIAlertController 弹窗

// UIAlertControllerStyleAlert 弹框, UIAlertControllerStyleActionSheet 底部弹框
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"是否确认注销?" message:@"注销后将返回登录" preferredStyle:UIAlertControllerStyleActionSheet];
// 2.创建并添加按钮
UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
    NSLog(@"OK Action");
//        alert
}];
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
    NSLog(@"Cancel Action");
}];
UIAlertAction *aAction = [UIAlertAction actionWithTitle:@"Aaaa" style:UIAlertActionStyleDestructive handler:^(UIAlertAction * _Nonnull action) {
    NSLog(@"Cancel Action");
}];
// 2.1 添加文本框  只有 UIAlertControllerStyleAlert 样式可以添加输入框
//    [alert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
//        textField.placeholder = @"username";
////        [textField addTarget:self action:@selector(alertUserAccountInfoDidChange:) forControlEvents:UIControlEventEditingChanged];     // 添加响应事件
//    }];
[alert addAction:okAction];
[alert addAction:cancelAction];
[alert addAction:aAction];
[self presentViewController:alert animated:YES completion:^{

}];
UIAlertControllerStyleAlert 样式

UIAlertControllerStyleAlert

UIAlertControllerStyleActionSheet 样式

UIAlertControllerStyleActionSheet

presentViewController 打开新页面

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController
    - (void)viewDidLoad {
        [super viewDidLoad];
        self.view.backgroundColor = [UIColor redColor];
    }

    - (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        UIViewController *vc = [[UIViewController alloc] init];
        vc.view.backgroundColor = [UIColor blueColor];
        // 设置全屏切换页面, 默认 UIModalPresentationAutomatic 顶部透明
        vc.modalPresentationStyle = UIModalPresentationFullScreen;
        // 设置切换动画, 默认从下往上
        vc.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
        // 跳转到其他ViewController
        [self presentViewController:vc animated:YES completion:^{
            NSLog(@"完成跳转");
        }];

        // 关闭当前ViewController
    //    [self dismissViewControllerAnimated:YES completion:^{
    //        NSLog(@"%s", __func__);
    //    }];
    }
@end