@(iOS Study)[实战技术]
- 作者: Liwx
- 邮箱: [email protected]
目录
- 02.实战技术 父子控制器,常量Const,开发中常见问题
- 开发中常见问题/常量Const/父子控制器
- 1.super,superClass,class关键字
- super,superClass,class介绍
- super关键字
- 2.项目奇怪的bug
- bug1. 已添加文件到项目中,却提示未定义
- bug2. 重复添加文件到项目中
- bug3. 导入.m文件
- 3.const的使用
- const与宏的区别
- const的简单使用
- const开发中使用场景
- static和extern使用
- static和const联合使用
- extern和const联合使用
- 4.父子控制器
- 父子控制器简介
- 父子控制器相关知识点
- 父子控制器的简单使用
- 5.开发中常见问题
- UIScrollView的自动布局
- UIScrollView自动布局步骤(重要)
- size和center设置注意点
- bounds和frame简介
- bounds和frame介绍
开发中常见问题/常量Const/父子控制器
1.super,superClass,class关键字
super,superClass,class介绍
- 关键字在方法中的意义
self: 方法调用者
class: 获取方法调用者的类对象
superclass:获取方法调用者的父类对象
super关键字
-
super关键字的介绍
- super:不是一个指针,编译指示器(标识符)
- super的本质:其实还是当前对象去调用,只不过让当前对象去调用父类方法
- super不是父类对象,指的是父类方法
注意
:[super class]表示用self调用父类方法,[super superclass]表示用self调用父类的方法(获取父类方法)super的案例说明
@implementation SubPerson
- (void)test
{
// [super class]表示用self调用父类方法,[super superclass]表示用self调用父类的方法(获取父类方法)
NSLog(@"%@ %@ %@ %@",[self class], [self superclass], [super class], [super superclass]);
// 正确打印结果: SubPerson Person SubPerson Person √
// 错误打印结果: SubPerson Person Person NSObject X
}
@end
2.项目奇怪的bug
bug1. 已添加文件到项目中,却提示未定义
- 已添加文件到项目中,却提示未定义bug
- 导致该bug的原因
- 在创建文件/从其他地方拖进工程的时候,没有勾选以下选项
创建的时候未勾选该选项,导致没添加到编译列表
- 在创建文件/从其他地方拖进工程的时候,没有勾选以下选项
从其他地方拖进工程的时候,没有勾选Targets项,导致没添加到编译列表
- 解决方案: 在
项目文件/Build Phases/Complie Sources
将文件(.m文件)
添加到编译列表
bug2. 重复添加文件到项目中
- 重复添加文件,提示错误
- 解决方案: 找出重复文件,将文件移除.
bug3. 导入.m文件
- 不小心导入了.m文件
3.const的使用
const与宏的区别
-
const与宏的区别
- 1.
编译时刻
:const:编译时期 宏:预编译时期
- 2.
编译检测
: const有编译检测,宏没有编译检测.如#define WXKey @"123"488 ,这样宏不会提示错误. - 3.
宏可以替换方法和函数,const不行
- 4.
大量使用宏,容易造成预编译时期过长
. - 有博客提到:大量使用宏,会导致内存暴增,有异议.
- 1.
苹果使用const为了迎合swift
'#'号是预编译指令
- 预编译时期:项目一打开的时候,有个
自动读条时期
,这个时期就是在做预编译操作
,如果定义了大量的宏,可能会导致自动读条的时间变长,是开发效率变低,苹果官方建议尽量少使用宏
.
const的简单使用
-
const的作用
- 1.用来
修饰右边
变量(基本变量,指针变量,对象等) - 2.只要被
const修饰的变量,只读
- 1.用来
-
const使用原则
-
放在const右边被修饰的变量为只读
.
-
const笔试题
// 1.原因:const修饰的是p,所以p为只读;const在*之后,const没修饰*p,所以*p是变量.
int * const p; // p:只读 *p:变量
// 2.原因: const修饰的是*p1,所以*p1为只读;const没修饰p1,所以p1是变量.
int const *p1; // p1:变量 *p1:只读
// 3.原因: const修饰*p2,所以*p2为只读;const没修饰p2,所以p2为变量
const int *p2; // p2:变量 *p2:只读
// 4.原因: 第一个const修饰*p3,所以*p3为只读;第二个const修饰p3,所以p3为只读
int const * const p3; // p3:只读 *p3:只读
// 5.原因: 第一个const修饰*p4,所以*p4为只读;第二个const修饰p4,所以p4为只读
const int * const p4; // p4:只读 *p4:只读
const开发中使用场景
- 1.当代码多处(不同方法中)共用同一个只读(无需修改)的字符串,比如多处(不同方法中)用到setValue:forKey:时都需要用到同一个key时,可以使用以下方法定义const字符串常量.
NSString * const key = @"key";
static和extern使用
-
static和extern的作用
- static作用:
1.描述局部变量,局部变量被
static修饰,生命周期延长(整个app运行过程中都在),作用域不变
.
2.局部变量被static修饰,只会分配一次内存,程序一启动就会分配
.
3.修饰全局变量,全局变量被static修饰,生命周期不变,作用域会变,只能在当前文件下使用
.- extern作用:
声明
一个全局变量,不能定义变量
.
static和const联合使用
- static和const联合使用场景
// 用于KVC的key时,可以static和const联合使用
static NSString * const ageKey = @"age";
extern和const联合使用
开发规范
:全局变量不能在自己的文件下定义,搞一个专门文件,去管理所有全局变量-
extern和const联合使用使用场景(声明全局常量的时候)
- 创建Const.h和Const.m文件用来存放全局常量
- 在Const.h中使用extern和const声明 NSString * const nameKey;
注意
:不能在.h文件中赋值. - 在Const.m中定义NSString * const nameKey = @"name";
开发中存放常量的文件中,常量可以按功能模块划分.
// Const.m
#import "Const.h"
/*********我的********/
NSString * const nameKey = @"nameKey";
/*********发现********/
NSString * const discoveryKey = @"discoveryKey";
// Const.h
/*********我的********/
extern NSString * const nameKey;
/*********发现********/
extern NSString * const discoveryKey;
- 模仿系统实现extern
// Const.h
// WXKIT_EXTERN相当于extern
#define WXKIT_EXTERN extern __attribute__((visibility ("default")))
WXKIT_EXTERN NSString * const nameKey;
4.父子控制器
父子控制器简介
父子控制器应用场景-
多控制器管理者
:导航控制器,tabBarController,内部原理就是采取父子控制器去管理
.
任何控制器都可以成为父控制器
父子控制器设计原则:把A控制器的view添加到B控制器的view上,那么A控制器必须要成为B控制器子控制器
.
父子控制器相关知识点
- 调试时
已知问题可以先列出来
,以防忘记,遗漏处理已经存在的问题
/*
问题:
1.每次都创建控制器
2.每次都添加view
3.控制器被销毁
*/
-
调用dismissViewControllerAnimated:completion:方法时,dismiss实现内部实现
- 1.判断下当前控制器是不是modal,如果是,就会dismiss
- 2.如果不是,判断下父控制器是不是modal,如果是,就会dismiss
- 3.继续判断,直到没有
-
只有
导航控制器子控制器才能获取self.navigationController
.- 使用
self.navigationController
时,会判断自己是不是导航控制器子控制器
,如果不是,判断自己的父控制器是不是导航控制器子控制
器,继续判断,直到没有父控制器.
- 使用
父子控制器的简单使用
- 父子控制的简单使用
#import "ViewController.h"
#import "SocietyViewController.h"
#import "TopLineViewController.h"
#import "HotViewController.h"
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIView *topView;
@property (nonatomic, weak) UIButton *selectedBtn;
@property (weak, nonatomic) IBOutlet UIView *contentView;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 1.添加所有子控制器
[self setUpAllChildViewController];
// 2.通过控制器标题,设置按钮显示的文字
[self setupTopButton];
}
// ----------------------------------------------------------------------------------
// 初始化
#pragma mark - 初始化
/** 初始化顶部button */
- (void)setupTopButton
{
NSInteger count = self.topView.subviews.count;
for (NSInteger i = 0; i < count; i++) {
// 取出button
UIButton *btn = self.topView.subviews[i];
// 取出子控制器
UIViewController *vc1 = self.childViewControllers[i];
// 设置btn显示的文字
[btn setTitle:vc1.title forState:UIControlStateNormal];
// 设置btn选中状态的文字颜色,需在storyboard中将按钮Type修改为Custom,否则系统默认会把按钮渲染成蓝色
[btn setTitleColor:[UIColor redColor] forState:UIControlStateSelected];
// 设置默认选中
if (i == 0) {
[self btnClick:btn];
}
}
}
/** 添加所有子控制器 */
- (void)setUpAllChildViewController
{
// 1.添加社会控制器
SocietyViewController *vc1 = [[SocietyViewController alloc] init];
vc1.title = @"社会";
[self addChildViewController:vc1];
// 2.添加头条控制器
TopLineViewController *vc2 = [[TopLineViewController alloc] init];
vc2.title = @"头条";
[self addChildViewController:vc2];
// 3.添加热点控制器
HotViewController *vc3 = [[HotViewController alloc] init];
vc3.title = @"热点";
[self addChildViewController:vc3];
}
// ----------------------------------------------------------------------------------
// 添加顶部三个按钮的监听
#pragma mark - 事件监听
/** 监听顶部三个button的点击 */
- (IBAction)btnClick:(UIButton *)btn {
// 1.切换选中状态
[self changeSelectedBtn:btn];
// 2.设置子控制器view的属性
// 2.1 根据button的tag值,取出对应的控制器
UIViewController *vc = self.childViewControllers[btn.tag];
// 2.2 设置控制器view的背景颜色和button的背景颜色一样
vc.view.backgroundColor = btn.backgroundColor;
// 2.3 设置子控制器的view的frame
vc.view.frame = self.contentView.bounds;
// 3.将子控制器的view添加到contentView
// 3.1 先移除当前控制器view的所有子控件
[self.contentView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
// 3.2 添加子控件的view到当前控制器的contentView上
[self.contentView addSubview:vc.view];
}
/** 切换按钮选中状态 */
- (void)changeSelectedBtn:(UIButton *)btn
{
self.selectedBtn.selected = NO;
self.selectedBtn = btn;
self.selectedBtn.selected = YES;
}
@end
- NSArray类的
makeObjectsPerformSelector
方法的使用
// 数组调用该方法时,表示数组里每个元素会执行test方法
[arr makeObjectsPerformSelector:@selector(test)];
-
UIButon的Type如果是System
,系统默认会把按钮渲染成蓝色
5.开发中常见问题
UIScrollView的自动布局
-
UIScrollView的自动布局思考
- 1.确定好
UIScrollView的滚动范围contentSize
- 2.怎么确定?
搞一个view添加到UIScrollView上,这个view用来确定UIScrollView的滚动范围
- 3.通过
设置这个view约束.来告诉UIScrollView滚动范围
- 4.如何计算contentSize呢?
要计算contentSize必须清楚每个subviews的frame
,而subviews的frame居然又要依赖scrollView
?
- 1.确定好
-
UIScrollView的自动布局原则
- 1.
UIScrollView的contentSize依赖于subviews
. - 2.UIScrollView的
contentSize必须根据其子控件如UIView的4条边来确定
. - 3.
注意
:如果只设置UIScrollView子控件的上下左右间距为0,是不能确定contentSize的,必须设置其子控件UIView的尺寸才能确定4条边.
- 1.
UIScrollView自动布局步骤(重要)
- UIScrollView自动布局实现步骤
- 1.
给UIScrollView添加一个UIView,作为UIScrollView的contentView.
之后要添加其他子控件可以往contentView上添加,这样UIScrollView就只有contentView一个子控件,便于布局 - 2.
设置contentView的尺寸(宽,高)
,因为UIScrollView的contentSize是由它的子控件的frame决定的
,contentView是UIScrollView的子控件,所以必须设置contentView的size才能确定contentSize
. - 3.设置
contentView的约束
,比如设置约束上下左右间距都为0.
- 1.
size和center设置注意点
size和center如果一起使用时,如果
设置的是frame的size,必须先设置尺寸frame.size,再设置center
.如果设置的是bounds的size,就无需考虑先后顺序
.因为center就是根据bounds中的size决定的
.设置frame的size属性,先设置尺寸frame.size,再设置center.
- (void)viewDidLoad {
[super viewDidLoad];
// 创建redView
UIView *redView = [[UIView alloc] init];
redView.backgroundColor = [UIColor redColor];
[self.view addSubview:redView];
// 先设置尺寸
CGRect frame = redView.frame;
frame.size = CGSizeMake(200, 200);
redView.frame = frame;
// 再设置center
redView.center = CGPointMake(self.view.bounds.size.width * 0.5, self.view.bounds.size.height * 0.5);
}
- 设置bounds的size属性,无需先后顺序.
- (void)viewDidLoad {
[super viewDidLoad];
// 创建redView
UIView *redView = [[UIView alloc] init];
redView.backgroundColor = [UIColor redColor];
[self.view addSubview:redView];
// 设置center
redView.center = CGPointMake(self.view.bounds.size.width * 0.5, self.view.bounds.size.height * 0.5);
// 设置尺寸
CGRect bounds = redView.bounds;
bounds.size = CGSizeMake(200, 200);
redView.bounds = bounds;
}
bounds和frame简介
bounds和frame介绍
-
frame和bounds的区别
-
frame
:以父控件的左上角为原点
-
bounds
:以自己左上角为原点
,x,y = 0,(X)
-
-
frame和bounds描述一个矩形
- frame:可视范围
- bounds:描述 可视范围 在 内容范围 区域
-
bounds本质:
修改内容原点
位置- 子控件是相对于控件
内容范围
frame:相对于父控件,位置永远不变
frame:相对于内容,就会改变
- 子控件是相对于控件