1.Segue的一些知识
在stroyboard中有如下控制器
viewController.m
#import "ViewController.h"
@interface ViewController ()
- (IBAction)blueClick:(id)sender;
- (IBAction)purClick:(id)sender;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//只要跳转都走了stroyboard线
}
//重写preparForSegue方法,走stroyboard线的跳转都会调用他,这个Suge就会有值,此外segue也带来了控制器的信息
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
//用父类接受各个不同的ViewController
UIViewController* viewController=segue.destinationViewController;
//判断Segue传来的到底是那个控制器
if ([[viewController isKindOfClass:[BlueViewController class]]) {
//与蓝色控制器有关的一系列代码
}else{
//其他控制器的一些代码
}
}
//蓝色按钮点击事件,手动执行segue
- (IBAction)purClick:(id)sender {
UIViewController* vc=[[UIViewController alloc]init];
vc.view.backgroundColor=[UIColor purpleColor];
[self.navigationController pushViewController:vc animated:YES];
}
- (IBAction)blueClick:(id)sender {
[self performSegueWithIdentifier:@"blue" sender:@"blue"];
//[self performSegueWithIdentifier:@"green" sender:@"green"];
}
@end
2.控制器的数据传递(逆传)
- 控制器的跳转方向A–>C
- 数据的传递方向:C–>A
- 数据的传递方式:让A称为C的代理,在C中调用A的代理方法,通过代理方法的参数传递数据给A
由于是C让A做事情,因此协议Protocol写在C中(做事的规范由C指定),而协议的实现方法在A中实现,最后由回到由C来调用这些实现的方法(因为是C让A做事情)
这里可以看出,谁调用代理方法,谁定义代理协议,这里C调用代理方法来做是请,因此在C里定义代理协议ProtocolXXX和代理属性delegate。A只是纯粹地实现代理方法(因为真正做事的是A)
创建两个ViewController,完成A到C的跳转,并完成C到A的数据传递
A对应ViewController A字母 代理方法的实现者
C对应TestViewController,红色背景,代理协议的创建者,代理属性的创建者,代理方法的调用者
ViewController.m
#import "ViewController.h"
#import "TestViewController.h"
@interface ViewController ()<TestViewControllerDelegate>//继承代理协议
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
#pragma mark -------实现代理方法---------
//实现代理方法
-(void)testViewController:(TestViewController *)testViewController withStr:(NSString *)str{
NSLog(@"%@",str);
}
//重写点击屏幕的方法
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
//创建TestViewController对象
TestViewController* viewController=[[TestViewController alloc]init];
//指定代理对象
viewController.delegate=self;
//跳转到新创建的控制器对象上
[self.navigationController pushViewController:viewController animated:YES];
}
@end
TestViewController.h
#import <UIKit/UIKit.h>
//这里要务必要引入TestViewController
@class TestViewController;
NS_ASSUME_NONNULL_BEGIN
//定义一个代理协议
@protocol TestViewControllerDelegate <NSObject>
@optional
//定义个代理方法,一般第一个参数都是自己,
-(void)testViewController:(TestViewController*)testViewController withStr:(NSString*)str;
@end
@interface TestViewController : UIViewController
//必不可少的代理属性
@property(nonatomic,weak)id<TestViewControllerDelegate> delegate;
@end
NS_ASSUME_NONNULL_END
TestViewController.m
#import "TestViewController.h"
@interface TestViewController ()
@end
@implementation TestViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor=[UIColor redColor];
}
//点击屏幕的方法
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
NSString* str=@"value";
//逆传,把B(test控制器)的值传给A(导航控制器)
//判断控制器是否能响应代理代理
if ([self.delegate respondsToSelector:@selector(testViewController: withStr:)]) {
//那就调用这个代理方法
[self.delegate testViewController:self withStr:str];
}
[self.navigationController popViewControllerAnimated:YES];
}
@end
效果:
跳转后打印出:value
3.控制器的数据传递(逆传)的步骤-----使用代理
使用代理的步骤
1、协议
2、协议里面的方法
3、delegate属性
4、在哪执行(实现)代理方法
5、在哪使用代理方法
4.控制器的数据传递(逆传)的步骤-----使用block
使用block的步骤
1、直接在写block属性(在C中)用以替代delegate属性,相当于使用代理时的(书写协议,书写协议里面的方法,创建delegate属性),block的写法参照实现代理方法来写。
2、block在哪执行
3、block在哪使用
A对应ViewController A字母 block方法的实现者
C对应TestViewController,红色背景,block方法的创建者,代理属性的创建者,代理方法的调用者
TestViewController.h
#import <UIKit/UIKit.h>
//这里要务必要引入TestViewController
NS_ASSUME_NONNULL_BEGIN
@interface TestViewController : UIViewController
//使用block块实现逆传,用以替代delegate属性
@property(nonatomic,copy) void(^strBlock)(NSString*);
@end
NS_ASSUME_NONNULL_END
TestViewController.m
#import "TestViewController.h"
@interface TestViewController ()
@end
@implementation TestViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor=[UIColor redColor];
}
//点击屏幕的方法
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
NSString* str=@"value";
//逆传,把B(test控制器)的值传给A(导航控制器)
//判断block里面是否有值
if (self.strBlock) {
self.strBlock(str);
}
[self.navigationController popViewControllerAnimated:YES];
}
@end
ViewController.m
#import "ViewController.h"
#import "TestViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//
}
//重写点击屏幕的方法
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
//创建TestViewController对象
TestViewController* viewController=[[TestViewController alloc]init];
//通过block方式来输出值
viewController.strBlock=^(NSString* mystr){
NSLog(@"%@",mystr);
};
//跳转到新创建的控制器对象上
[self.navigationController pushViewController:viewController animated:YES];
}
@end
效果:
显示:value
5.控制器的数据传递(顺传)
顺传的步骤
1、在B控制器里创建一个与要传过来的数据的类型对应(一致)的属性
2、在B控制器对象中直接赋值
**A对应ViewController A字母 **
B对应TestViewController,数据信息的接受者
TestViewController.h
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface TestViewController : UIViewController
//用来接收从A传来的数据
@property(nonatomic,copy)NSString* str;
@end
NS_ASSUME_NONNULL_END
TestViewController.m
#import "TestViewController.h"
@interface TestViewController ()
@end
@implementation TestViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor=[UIColor greenColor];
//打印出从A传来的值
NSLog(@"%@",self.str);
}
@end
ViewController.m
#import "ViewController.h"
#import "TestViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
//点击屏幕时调用的方法
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
NSString* str=@"myvalue";
//创建B控制器
TestViewController* viewController=[[TestViewController alloc]init];
viewController.str=str;
//跳转到新创建的控制器
[self.navigationController pushViewController:viewController animated:YES];
}
@end
效果:
控制台输出:2020-07-18 00:59:23.040278+0800 IOS073[4935:465335] myvalue
6.沙盒的根目录
根目录
NSString* home=NSHomeDirectory();
根目录下的子目录
分别是Documents,Library,temp
7.获取手机Documents目录
获取Document文件夹路径
- (IBAction)save:(id)sender {
//获取document路径
//1、拼接字符串形式获取document路径
/*
NSString* homePath=NSHomeDirectory();
NSString* docPath=[homePath stringByAppendingFormat:@"/Documents"];
//NSString* docPath=[homePath stringByAppendingPathComponent:@"Documents"];//这里不用斜杠
NSLog(@"%@",docPath);
*/
//2、搜索形式形式获取document路径,在哪个区域的那个文件夹,
//常用形式
NSString* docPath=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];//第三个参数为是否展开路径
NSLog(@"%@",docPath);
}
8.向沙盒Documents目录中写入一个plist文件
#import "ViewController.h"
@interface ViewController ()
- (IBAction)save:(id)sender;
- (IBAction)read:(id)sender;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
NSLog(@"%@",NSHomeDirectory());
}
//存数据
- (IBAction)read:(id)sender {
}
//取数据
- (IBAction)save:(id)sender {
//获取document路径
//1、拼接字符串形式获取document路径
/*
NSString* homePath=NSHomeDirectory();
NSString* docPath=[homePath stringByAppendingFormat:@"/Documents"];
//NSString* docPath=[homePath stringByAppendingPathComponent:@"Documents"];//这里不用斜杠
NSLog(@"%@",docPath);
*/
//2、搜索形式形式获取document路径,在哪个区域的那个文件夹,
//常用形式
NSString* docPath=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];//第三个参数为是否展开路径
//
NSString* filePath=[docPath stringByAppendingPathComponent:@"my.plist"];
//plist里面需要存储的数据,一个Array数组或者一个Dictionary字典
NSArray* array=@[@"张三",@"李四",@"王二",@"丁一"];
[array writeToFile:filePath atomically:YES];
NSLog(@"%@",docPath);
}
@end
效果:
内容:
9.向沙盒Documents目录中读取一个plist文件
数组组成的plist文件
#import "ViewController.h"
@interface ViewController ()
- (IBAction)save:(id)sender;
- (IBAction)read:(id)sender;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
NSLog(@"%@",NSHomeDirectory());
}
//存数据
- (IBAction)read:(id)sender {
//获取目录路径
NSString* docPath=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
//获取文件路径
NSString* filePath=[docPath stringByAppendingPathComponent:@"my.plist"];
//用一个array数组来接受文件数据
NSArray* array=[NSArray arrayWithContentsOfFile:filePath];
NSLog(@"%@",array);
}
//取数据
- (IBAction)save:(id)sender {
//获取document路径
//1、拼接字符串形式获取document路径
/*
NSString* homePath=NSHomeDirectory();
NSString* docPath=[homePath stringByAppendingFormat:@"/Documents"];
//NSString* docPath=[homePath stringByAppendingPathComponent:@"Documents"];//这里不用斜杠
NSLog(@"%@",docPath);
*/
//2、搜索形式形式获取document路径,在哪个区域的那个文件夹,
//常用形式
NSString* docPath=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];//第三个参数为是否展开路径
//
NSString* filePath=[docPath stringByAppendingPathComponent:@"my.plist"];
//plist里面需要存储的数据,一个Array数组或者一个Dictionary字典
NSArray* array=@[@"张三",@"李四",@"王二",@"丁一"];
[array writeToFile:filePath atomically:YES];
NSLog(@"%@",docPath);
}
@end
输出:
2020-07-18 13:04:26.797831+0800 IOS074[3693:151596] (
“\U5f20\U4e09”,
“\U674e\U56db”,
“\U738b\U4e8c”,
“\U4e01\U4e00”
)
字典组成的plist文件
#import "ViewController.h"
@interface ViewController ()
- (IBAction)save:(id)sender;
- (IBAction)read:(id)sender;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
NSLog(@"%@",NSHomeDirectory());
}
//存数据
- (IBAction)read:(id)sender {
//获取目录路径
NSString* docPath=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
//获取文件路径
NSString* filePath=[docPath stringByAppendingPathComponent:@"my.plist"];
//用一个array数组来接受文件数据
NSDictionary* dctionary=[NSDictionary dictionaryWithContentsOfFile:filePath];
NSLog(@"%@",dctionary);
}
//取数据
- (IBAction)save:(id)sender {
//获取document路径
//1、拼接字符串形式获取document路径
/*
NSString* homePath=NSHomeDirectory();
NSString* docPath=[homePath stringByAppendingFormat:@"/Documents"];
//NSString* docPath=[homePath stringByAppendingPathComponent:@"Documents"];//这里不用斜杠
NSLog(@"%@",docPath);
*/
//2、搜索形式形式获取document路径,在哪个区域的那个文件夹,
//常用形式
NSString* docPath=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];//第三个参数为是否展开路径
//
NSString* filePath=[docPath stringByAppendingPathComponent:@"my.plist"];
//plist里面需要存储的数据,一个Array数组或者一个Dictionary字典
NSDictionary* dictionary=@{@"name":@"zhagnsan",@"age":@34};
[dictionary writeToFile:filePath atomically:YES];
NSLog(@"%@",docPath);
}
@end
效果:
2020-07-18 13:09:05.800182+0800 IOS074[3721:154313] {
age = 34;
name = zhagnsan;
}
10.使用userDefaults存取数据
注意“使用Userdefaults设置数据时,不是立即写入,而是根据时间戳定时地把缓存中的数据写入bending磁盘,所以调用了set方法之后数据有可能还没有写入磁盘应用程序就终止了,如果出现以上问题,可以通过调用synchronize方法强制写入,方法为:[defaults synchornize];
使用userDefaults存取数据时
1、不需要关心文件夹和文件的名字
2、快速做键值对
3、用法和字典基本一致
#import "ViewController.h"
@interface ViewController ()
- (IBAction)save:(id)sender;
- (IBAction)read:(id)sender;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
NSLog(@"%@",NSHomeDirectory());
}
//存数据
- (IBAction)read:(id)sender {
//先获取userdefaults单例对象
NSUserDefaults* userdefaults=[NSUserDefaults standardUserDefaults];
NSLog(@"%@-----%@",[userdefaults objectForKey:@"key"],[userdefaults objectForKey:@"isOn"]);
}
//取数据
- (IBAction)save:(id)sender {
//创建一个 单例 的NSUserDefaults对象
NSUserDefaults* userdefaults=[NSUserDefaults standardUserDefaults];
[userdefaults setObject:@"value" forKey:@"key"];
[userdefaults setBool:YES forKey:@"isOn"];
[userdefaults synchronize];
}
@end
效果
2020-07-18 13:28:08.945672+0800 IOS074[3836:165675] value-----1
11.归档/解档(将一个对象的全部或部分属性存储到文件中)
归档:存一个对象
解档:取一个对象
1、创建一个Teacher类模型并实现nscoding协议
Teacher.h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface Teacher : NSObject<NSCoding>//必须遵守归档解档协议
@property(nonatomic,copy)NSString* name;
@property(nonatomic,assign)int age;
@end
NS_ASSUME_NONNULL_END
Teacher.m
#import "Teacher.h"
@implementation Teacher
//告诉系统需要归档那些属性
-(void)encodeWithCoder:(NSCoder *)coder{
[coder encodeObject:_name forKey:@"name"];//对name属性进行归档
[coder encodeInt:_age forKey:@"age"];
}
//告诉系统要解档哪些属性
-(instancetype)initWithCoder:(NSCoder *)coder{
if (self=[super init]) {
_name= [coder decodeObjectForKey:@"name"];
_age= [coder decodeIntForKey:@"age"];
}
return self;
}
@end
ViewController.m
#import "ViewController.h"
#import "Teacher.h"
@interface ViewController ()
- (IBAction)save:(id)sender;
- (IBAction)read:(id)sender;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (IBAction)read:(id)sender {
//获取一个temp目录对象
NSString* temPath= NSTemporaryDirectory();
//获取filePath
NSString* filePath=[temPath stringByAppendingPathComponent:@"teacher.data"];
//解档
Teacher* teaher=[NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
NSLog(@"%@----%d岁",teaher.name,teaher.age);
}
- (IBAction)save:(id)sender {
//归档
//获取一个temp目录对象
NSString* temPath= NSTemporaryDirectory();
//获取filePath
NSString* filePath=[temPath stringByAppendingPathComponent:@"teacher.data"];
//创建自定义对象
Teacher* teacher=[[Teacher alloc]init];
teacher.name=@"张老三";
teacher.age=34;
//归档类的归档方法
[NSKeyedArchiver archiveRootObject:teacher toFile:filePath];//该方法在ios12中被舍弃了
}
@end
结果:
先后点击save和read按钮
控制台输出:
2020-07-19 00:18:08.271766+0800 IOS075[1909:78021] 张老三----34岁
归档/解档示意图
12.使用plist存储字符(不建议用于单纯存储字符,且在存储中文有可能不能读取)
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSString* str=@"value";//使用中文无法读取,但plist不建议单纯存储字符串
NSString* tempPath=NSTemporaryDirectory();
NSString* filePath=[tempPath stringByAppendingPathComponent:@"myfile.plist"];
[str writeToFile:filePath atomically:YES];
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
//获取路径
NSString* tempPath=NSTemporaryDirectory();
NSString* filePath=[tempPath stringByAppendingPathComponent:@"myfile.plist"];
NSString* str=[NSString stringWithContentsOfFile:filePath];
NSLog(@"%@",str);
}
@end
13.UITabVarController
UITabVarController的简单实用步骤
1、初始化UITabVarController
2、设置UIWindow的rootViewController为UITabVarController
3、根据具体情况,通过addChildViewController方法添加对应个数的子控制器
如果UITabBarController有N个子控制器,那么UITabBar内部就会有N个UITabBarButton作为子控件
如果UITabBarController有4个子控制器,那么UITabBar的结构大致如下图:
UITabBarButton里面显示什么内容,由对应子控制器的UITabBarItem属性决定
UITabBarItem有以下属性
@property(nonatomic,copy) NSString* title;
@property(nonatomic,copy) NSString* image;
@property(nonatomic,copy) NSString* selectedImage;
@property(nonatomic,copy) NSString* badgeValue;
AppDelegate
#import "AppDelegate.h"
@interface AppDelegate ()
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// 创建window
self.window=[[UIWindow alloc]initWithFrame:[UIScreen mainScreen].bounds];//window好像在screenDelegate中管理了
//初始化tabVarController
UITabBarController* tabbarController=[[UITabBarController alloc]init];
//添加子控制器
//创建自控制器(实际上就是tab的三个按钮)
UITabBarController* v1=[[UITabBarController alloc]init];
UITabBarController* v2=[[UITabBarController alloc]init];
UITabBarController* v3=[[UITabBarController alloc]init];
//设置背景色
v1.view.backgroundColor=[UIColor redColor];
v2.view.backgroundColor=[UIColor greenColor];
v3.view.backgroundColor=[UIColor blueColor];
//设置内容
v1.tabBarItem.title=@"联系人";
v1.tabBarItem.image=[UIImage imageNamed:@"tab_buddy_nor"];
v2.tabBarItem.title=@"消息";
v2.tabBarItem.image=[UIImage imageNamed:@"tab_me_no"];
v3.tabBarItem.title=@"设置";
v3.tabBarItem.image=[UIImage imageNamed:@"tab_recent_nor"];
v3.tabBarItem.badgeValue=@"998";
//添加子控件
[tabbarController addChildViewController:v1];
[tabbarController addChildViewController:v2];
[tabbarController addChildViewController:v3];
//设置window的根控制器
self.window.rootViewController=tabbarController;
//显示
[self.window makeKeyAndVisible];
return YES;
}
@end
14.App主流UI框架结构
15.QQ界面
控制器关系
1、启动控制器TabBarController–>连接4个NavigatioController(拖线选中ViewControllers)
2、每个Navigation连接一个TableViewController并拖线选中rootController
3、给每个TableViewController设置不同的title或拖拽控件
4、进行一系列title和文字的设置,完成主界面的搭建
等等
5、一些列鼠标骚操作(无需代码),完成界面的设计
16.Modal控制器切换
modal是一种控制器切换方式,任何控制器都可以通过modal的形式展示出来,其默认效果是从屏幕的最底部上升,知道盖住之前的控制器为止。
1、创建FRViewController
FRViewController.m
#import "FRViewController.h"
@interface FRViewController ()
@end
@implementation FRViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor=[UIColor greenColor];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
//关闭控制器
[self dismissViewControllerAnimated:YES completion:^{
NSLog(@"已经关闭了");
}];
}
@end
2、在ViewController中设置跳转
ViewController.m
#import "ViewController.h"
#import "FRViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
//创建一个跳转到FRViewController对象
FRViewController* frvc=[[FRViewController alloc]init];
//执行跳转,后面的块是跳转完要去做的事情
[self presentViewController:frvc animated:YES completion:^{
NSLog(@"跳转完成");
}];
}
@end