目录:
1.block的基本使用
2.ScrollView的底层实现
3.Bounds和Frame简介
4.assign和weak的区别
5.枚举中的位运算
6.Size和Center
7.通知的补充
8.通知多线程使用
block的基本使用
- 1.block声明
block声明: 返回值(^block变量名)(参数)
#void(^block)() --->无参无返回值
- 2.block定义:三种方式 =^(参数){}
// 第一种
void(^block1)() = ^{ };
// 第二种 如果没有参数,参数可以隐藏,如果有参数,定义的时候必须写参数,而且必须要有参数变量名
void(^block2)(int) = ^(int = a){ };
// 第三种 block返回可以省略,不管有没有返回值,都可以省略
int(^block3)() = ^int{
return 3;
}; - 3.block类型
// block类型: int(^)(NSString *)
int(^block4)(NSString *) = ^(NSString *name){
return 2;
}; - 4.block调用
block1(); - 5.block快捷方式
inlineBlock - c Inline Block as Variable
ScrollView的底层实现
- 思路分析:
1.scrollView上下滚动时scrollView没有滚动,是上面的内容在滚动,通过该bounds实现滚动,
用代理方法去验证,设置代理,遵守协议,在crollViewDidScroll:(UIScrollView *)scrollView打印
NSLog(@"%@",NSStringFromCGRect(scrollView.bounds));,验证结果显示,bounds的y值一直在变化,
其实就是偏移量,向上移动时,y值增加,向下移动时,y值减少。在打印NSLog(@"%@",NSStringFromCGPoint(scrollView.contentOffset));
进行对比,验证表明,偏移量就是从bounds中取的的。
2.当我们手指网上拖时,内容往上走,y值是+,可视范围往下走。
3.通过,点击UIScrollView查看底层,我们知道了scrollView要想滚动,是因为加上了两个手势,一个Pan,一个捏合手势。
- 思路演示
模仿系统控件 ==>怎么去用 ==> 滚动scrollView其本质是在滚动内容 ==>改bounds ==>验证
==>手指网上拖动,bounds y++ ,内容才会往上走 - 代码片段①
#import "ViewController.h"
@interface ViewController ()<UIScrollViewDelegate>
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 模仿系统控件 ==>怎么去用 ==> 滚动scrollView其本质是在滚动内容 ==>改bounds ==>验证
// ==>手指网上拖动,bounds y++ ,内容才会往上走
UIScrollView *scrollView = [[UIScrollView alloc]initWithFrame:self.view.bounds];
scrollView.contentSize = CGSizeMake(0, 1000); // 内容在滚content
scrollView.delegate = self;
[self.view addSubview:scrollView];
UISwitch *switchView = [[UISwitch alloc]init];
[scrollView addSubview:switchView];
}
#pragma mark - UIScrollViewDelegate
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
NSLog(@"%@",NSStringFromCGRect(scrollView.bounds));
NSLog(@"%@",NSStringFromCGPoint(scrollView.contentOffset));
}
- 用UIView模仿ScrollView
#import "ViewController.h"
@interface ViewController ()<UIScrollViewDelegate>
@property(strong,nonatomic)UIView *scrollView;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
UIView *scrollView = [[UIView alloc]initWithFrame:self.view.bounds];
[self.view addSubview:scrollView];
_scrollView = scrollView;
// 要想移动--添加手势
// 1.添加Pan手势
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(pan:)];
[scrollView addGestureRecognizer:pan];
UISwitch *switchView = [[UISwitch alloc]init];
[scrollView addSubview:switchView];
}
// pan手势
- (void)pan:(UIPanGestureRecognizer *)pan
{
/* 每次拖动的时候,都会改动bounds,但是我们应该指导你拖 动了多少
当往上拖时, NSLog(@"%@",NSStringFromCGPoint(transP));,经过打印
y是负的,按照正常来说y,网上拖应该是正的。这时候我们就要取反。继而
bounds.origin.y -= transP.y;但是每次减transP.y,也会出现问题,使scrollView会
滚的很远。所以要做个复位。
*/
>
// 1. 获取手指的偏移量是多少---参数:手指在哪个View上的点
CGPoint transP = [pan translationInView:pan.view];
NSLog(@"%@",NSStringFromCGPoint(transP));
// 2.修改bounds
CGRect bounds = _scrollView.bounds;
bounds.origin.y -= transP.y;
_scrollView.bounds = bounds;
// 3.复位
[pan setTranslation:CGPointZero inView:pan.view];
}
Bounds和Frame简介
- Frame:以父控件左上角为原点
- Bounds:以自己的左上角为原点,boundsx,y永远为0,这个是错误的
1.当我们修改某控件bounds的x,y坐标时,对于自己的位置是不会发生变化的。但是对,自己内部的子控件,还是有影响的。
(说明了bounds的x,y是可以加减的,并不是永远都为0。x,y坐标的改变,只针对于自己的子控件。) - frame和bounds都是用来描述一块区域
frame:描述的可视范围
bounds:可视范围,在内容的区域。
所有的子控件都是相对于内容的。
bounds:本质是修改了内容的原点。
相对性:
可视范围相对于父控件的位置永远不变
可视范围相对于内容,位置改变
assign和weak的区别
- 解释weak,assgin什莫时候使用weak和assgin
weak: (__weak修饰)弱指针,不会让引用计数器+1,如果指向的对象被销毁,指针会自动清空。
ARC: 才有weak,这个东西。
MRC: 没有weak
assgin:(_unsafe_unretained修饰),不会让引用计数器+1,如果指向的对象被销毁,指针不会清空。会造成僵尸对象现象。
枚举中的位运算
只要枚举中,有位运算就可以使用并运算 |
- 为什莫?
代码:如一个方法监听了两个事件,编辑时,和值改变时。
//1 << n,2^n 左移
int a = 1 << 0; // 1
int b = 1 << 1; // 2 #①
int c = 1 << 2; // 4
int d = 1 << 3; // 8
- (void)viewDidLoad {
[super viewDidLoad];
[_textField addTagrget:self action:@selector(textBegin) forControlEvents: UIConrtolEventEditingDidBegin | UIControlEventEditingChanged]
>
[self test:a | b]; #②把位运算传进去
}
>
- (void)textBegin{
NSLog(@"开始编辑的时候会调用");
}
- (void)test: (int)value{
// 解析:value,判断下是否包含a,b,c,d
// &解析有没有包含a,b, c,d
// NSLog(@"%d %d %d %d",value & a,value & b,value & c,value & d};
if (value & a)NSLog(@"包含了a");
if (value & b)NSLog(@"包含了b");
if (value & c)NSLog(@"包含了c"); #③ 解析
if (value & d)NSLog(@"包含了d");
}
如图:
[里面去做判断===》forControlEvents]
Size和Center
- 先设置尺寸在设置center,否则控件控件的位置会不准确
- 用frame设置时,先设置center,后设置size,会导致控件位置不准确,跑偏
- 用bounds,没事
# 推荐:先设置size在设置center
-
原因:(如图)
frame是从左上角,控件向下扩展
bounds是从中心点慢慢扩大
- 建议:
如果size,从frame取出来,先设置size,在设置center
如果size,是从bounds取出来,就不用考虑center和size的区别
通知的补充
- 通知(要学习的点):
1.如何发出通知
2.监听通知
3.通知注意点
- 代码:
(方式一)
- (void)viewDidLoad{
[super viewDidLoad];
// 1.发出通知
// Name:通知名称
// object:谁发出的通知
[[NSNotificationCenter defaultCenter] postNotificationName: @"note" object:nil]; // 匿名发送
// 2.监听通知
// addObserver:谁监听
// selector: 只要一监听到通知,就会调用观察者这个方法
// Name:通知名称
// object:谁发出的通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(reciveNote) name:@"note" object:nil];
}
>
- (void)reciveNote{
NSLog(@"接受到通知");
}
// 一个对象即将销毁的时候就会调用
- (void)dealloc{
// 3.移除通知
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
方式二
// 监听通知
/**
Name:通知名称
object:谁发出的通知
queue:队列
usingBlock:只要监听到通知,就会调用block
*/
@property(nonatomic ,weak) id observe;
id observe = [[NSNotificationCenter defaultCenter] addObserverForName:@"note" object:nil
queue:nil usingBlock:^(NSNotification * _Nonull note){
// 只要监听到通知,就会调用block
NSLog(@"%@",[NSThread currentThread]);
NSLog(@"%@",self);
]};
系统观察,怎么去移除
- (void)dealloc{
// 3.移除通知
[[NSNotificationCenter defaultCenter] removeObserver:_observe];
}
上面一定监听不到通知,2要跟1调换顺序,先监听通知,发出通知。
-
注意点
1.通知顺序:一定要先监听,在发出
bug:监听不到通知,马上想到有可能先发出通知,在监听通知
2.一定要移除
通知多线程使用
- 异步线程,不能监听到通知
- 异步任务,执行顺序不确定
- 下面代码验证:异步线程可以监听通知
- (void)viewDidLoad{
[super viewDidLoad];// 2.监听通知 // addObserver:谁监听 // selector: 只要一监听到通知,就会调用观察者这个方法 // Name:通知名称 // object:谁发出的通知 dispatch_async(dispatch_get_global_queue(0,0),^{ // 异步任务 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(reciveNote) name:@"note" object:nil]; };
}
- (void)touchesBegan:(NSSet<UITouch *>*)touches withEvent:(UIEvent *)event{
// 1.发出通知
// Name:通知名称
// object:谁发出的通知
[[NSNotificationCenter defaultCenter] postNotificationName: @"note" object:nil]; // 匿名发送
}
// 监听到通知就会调用
// 异步:监听通知 主线程:发出通知 接受通知代码在主线程
// 在接受通知代码中,可以加上主队列任务
// 总结:监听通知代码 由发布通知线程决定
- (void)reciveNote{
NSLog(@"接受到通知");
dispatch_sync(dispatch_get_main_queue(),^{
// 更新UI
});
}
// 一个对象即将销毁的时候就会调用
- (void)dealloc{
// 3.移除通知
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- 总结:
1. 异步:监听通知 主线程:发出通知 接受通知代码在主线程
2. 在接受通知代码中,可以加上主队列任务
3. 总结:监听通知代码 由发布通知线程决定