美文网首页iOS进阶面试iOS之功能细节
iOS 开发中你是否遇到这些经验问题(二)

iOS 开发中你是否遇到这些经验问题(二)

作者: 判若两人丶 | 来源:发表于2016-08-14 17:45 被阅读8724次

前言:

在上一篇文章中我相信帮助了很多的小伙伴, 那么在这篇文章希望还能帮助到你!
相关文章:
iOS开发中你是否遇到这些经验问题(一)

1.在Block中一起使用weakSelf与strongSelf的含义

我们都会声明一个弱引用在block中使用, 目的就是防止循环引用, 那么weakSelfstrongSelf一起使用目的是什么呢? 首先先定义2个宏:

#define LRWeakSelf(type)  __weak typeof(type) weak##type = type;
#define LRStrongSelf(type)  __strong typeof(type) type = weak##type;

我们创建一个shop并且在shop.myBlock代码块中使用弱引用LRWeakSelf(shop);

    LRShop *shop = [[LRShop alloc]init];
    shop.string = @"welcome to our company";
    //弱引用
    LRWeakSelf(shop);
    shop.myBlock = ^{
        NSLog(@"%@",weakshop.string);
    };
    shop.myBlock();

LRWeakSelf(shop);LRStrongSelf(shop);一起使用

LRShop *shop = [[LRShop alloc]init];
    shop.string = @"welcome to our company";
    //弱引用
    LRWeakSelf(shop);
    shop.myBlock = ^{
        //强引用
        LRStrongSelf(shop)
        NSLog(@"%@",shop.string);
    };
    shop.myBlock();

这2个打印结果都是shop.string有值并且shop也销毁了, 看起来是没什么区别:

Log.png
仅仅使用LRWeakSelf(shop);并且在myBlock中增加一个延迟3秒在输出就会出现问题, 虽然对象销毁了, 输出的值却是null
//弱引用
 LRWeakSelf(shop);
    shop.myBlock = ^{
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"%@",weakshop.string);
        });
    };
    shop.myBlock();
Log.png

如果LRWeakSelf(shop);LRStrongSelf(shop);一起使用输出的shop.string有值,对象也销毁了, 我就不再截图给大家看了!

//弱引用
 LRWeakSelf(shop);
    shop.myBlock = ^{
        //强引用
        LRStrongSelf(shop)
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"%@",shop.string);
        });
    };
    shop.myBlock();

通过上面一堆的解释, 我们明显发现LRWeakSelf(shop);LRStrongSelf(shop);一起使用的好处, 不但能打印出我想要的值,而且也不会造成循环引用 , 在开发中这两个方法可以根据实际情况进行使用!

2.使用UIAppearance注意的问题

如果不熟悉可以点击了解, UIAppearance它的目的就是设置全局显示样式, 我们知道只要带UI_APPEARANCE_SELECTOR这个宏, 我们就可以使用UIAppearance比如这样设置:

UIAppearance.png

我们知道UIBarButtonItem它是有状态的比如UIControlStateNormal或者是UIControlStateDisabled状态
如果通过UIAppearance设置UIControlStateDisabled状态下的颜色是不好使的, 因为使用appearance会有一些延迟, 导致在不同状态下的颜色不好使, 我们只要强制刷新一下就可以了:

// 刷新
[self.navigationController.navigationBar layoutIfNeeded];

所以以后使用UIAppearance在某个状态下设置颜色,字体等不好使, 只需要在对应的位置用layoutIfNeeded刷新一下就可以了!

3. UITextField使用注意

先贴一个UITextField如何设置占位文字的颜色, 如果不先设置占位文字, 占位文字的颜色是不管用的:

//先设置占位文字
textField.placeholder = @"设置了占位文字内容以后, 才能设置占位文字的颜色";
//占位文字颜色
[textField setValue:[UIColor redColor] forKeyPath:@"_placeholderLabel.textColor"];

大家监听UITextField文字的改变会用到代理:

#pragma mark - <UITextFieldDelegate>
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
//这里监听文字改变
    return YES;
}

但是这个代理方法监听会有问题如下图:


1.png 2.png
所以我们要监听UITextField的文字改变不建议使用代理, 我们用addTarget监听文字
[textField addTarget:self action:@selector(textEditingChanged) forControlEvents:UIControlEventEditingChanged];
4.UITextView添加占位文字的正确方法

UITextView的占位文字属于它内部的一个功能, 我们在控制器或者用代理来处理占位文字一些功能是不合理的, 所以我们要自定义UITextView把相关内部的东西都封装起来!
(1)给外界提供占位文字与占位文字颜色:

/** 占位文字 */
@property (nonatomic, copy)NSString *placeholder;
/** 占位文字颜色 */
@property (nonatomic, strong)UIColor *placeholderColor;

(2)设置占位文字的默认值, 如果不设置默认值,外界不用你提供的方法会有崩溃现象:

// 设置默认字体
self.font = [UIFont systemFontOfSize:17];
// 设置默认的占位文字颜色
self.placeholderColor = [UIColor grayColor];

(3)内部添加占位文字的label ;

/** 占位文字label */
@property (nonatomic, weak) UILabel *placeholderLabel;

//懒加载
- (UILabel *)placeholderLabel
{
    if (_placeholderLabel == nil) {
        UILabel *placeholderLabel = [[UILabel alloc] init];
        placeholderLabel.numberOfLines = 0;
        [self addSubview:placeholderLabel];
        _placeholderLabel = placeholderLabel;
    }
    return _placeholderLabel;
}

(4)通过监听文字改变,来显示或隐藏占位文字

 // 监听文字
 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textDidChangeNotification) name:UITextViewTextDidChangeNotification object:nil];

//监听的方法
- (void)textDidChangeNotification {
    // 有文字就隐藏占位文字
    self.placeholderLabel.hidden = self.hasText;
}

(5)如果占位文字被修改, 颜色被修改, 字体被修改, 我们在内部需要重写set方法, 如果通过代码修改了textView文字(不是占位文字)不会发通知也需要重写set方法:

textView.png
封装好的自定义TextView可以直接使用:Demo下载
5.自定义控件里如何拿到导航控制器进行页面跳转?

(1)如果有UITabBarController我们会这样获取导航控制器:

 UIViewController *viewC = [[UIViewController alloc]init];
 // 取出当前的导航控制器
 UITabBarController *tabBarVc = (UITabBarController *)[UIApplication sharedApplication].keyWindow.rootViewController;
 //The view controller associated with the currently selected tab item
 //当前选择的导航控制器
 UINavigationController *navC = (UINavigationController *)tabBarVc.selectedViewController;
 [navC pushViewController:viewC animated:YES];

(2)如果通过modal出来的控制器并且用UITabBarController不好使, 我们会这样获取导航控制器:

 UIViewController *viewC = [[UIViewController alloc]init];
 //获取最终的根控制器
 UIViewController *rootC = [UIApplication sharedApplication].keyWindow.rootViewController;
 //如果是modal出来的控制器,它就会通过presentedViewController拿到上一个控制器
 UINavigationController *navC = (UINavigationController *)rootC.presentedViewController;
 [navC pushViewController:viewC animated:YES];
6.修改了leftBarButtonItem如何恢复系统侧滑返回功能

在开发中系统的leftBarButtonItem不是我们想要的, 如果我们修改了leftBarButtonItem那么系统自带的侧滑返回功能就不好使了!

leftBarButtonItem.png
//设置代理
self.interactivePopGestureRecognizer.delegate = self;
#pragma mark - <UIGestureRecognizerDelegate>
//实现代理方法:return YES :手势有效, NO :手势无效
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
    //当导航控制器的子控制器个数 大于1 手势才有效
    return self.childViewControllers.count > 1;
}
7.重新认识Bounds

我们之前对Bounds理解就是以自己的左上角为坐标原点, 也就是说Boundsxy值是0, 但是Boundsxy值有可能是正数也可能是负数, 不一定是0那么Bounds真正是什么意思呢 ?

  • Bounds: 是以自己内容的左上角为坐标原点, 计算出自己的位置和大小
  • Frame: 是以父类内容的左上角为坐标原点, 计算出自己的位置和大小
    那什么是内容呢 ? 首先内容是抽象的, 一个控件不仅仅只有一层矩形框的, 他有很多图层的, 这个内容其实就可以抽象成一个控件的内部图层
    内容:就是内部的东西, 它的子控件也属于内容,也就是说修改了Buonds子控件的位置也会跟着改变
    bounds.png
    上图蓝色和绿色是属于一个控件, 只不过蓝色是控件本身, 绿色是控件的内容, 我们改变这个控件的Boundsxy值为-20, 内容位置改变, 控件本身位置不变!
8.枚举中使用<<(左移)
<<.png
上图是一个苹果官方的一个枚举, 我们主要是看<<(它是c语言中的位运算左移的意思)的用处, 如果在枚举中只要<<那它的含义就是可以通过|进行组合使用:
 //随便添加一个UITextField
    UITextField *field = [UITextField new];
    //可以通过 | 组合使用UIControlEventEditingDidBegin, UIControlEventValueChanged,UIControlEventEditingDidEnd
    [field addTarget:self action:@selector(textFieldDidChanged) forControlEvents:UIControlEventEditingDidBegin | UIControlEventValueChanged | UIControlEventEditingDidEnd];
    [self.view addSubview:field];

如果枚举没有<<就不能组合使用, 那它有什么规律呢1 << n 代表:2的n次方:

//1 << 16 代表:2的16次方
 UIControlEventEditingDidBegin = 1 << 16,
//1 << 17 代表:2的17次方     
 UIControlEventEditingChanged  = 1 << 17,
//1 << 18 代表:2的18次方
 UIControlEventEditingDidEnd  = 1 << 18,
//1 << 19 代表:2的19次方
 UIControlEventEditingDidEndOnExit  = 1 << 19,  

原来这样的枚举可以组合使用, 那苹果官方是怎么知道我们多个条件组合使用了呢 ?

 NSUInteger controlEvents = UIControlEventEditingDidBegin | UIControlEventValueChanged | UIControlEventEditingDidEnd;
    /**
    //通过 & 符号来判断是否包含:
    UIControlEventEditingDidBegin,
    UIControlEventValueChanged,
    UIControlEventEditingDidEnd
     */
    if (controlEvents & UIControlEventEditingDidBegin) {
        
        NSLog(@"UIControlEventEditingDidBegin");
        
    }else if (controlEvents & UIControlEventValueChanged) {
        
        NSLog(@"UIControlEventValueChanged");
        
    }else if (controlEvents & UIControlEventEditingDidEnd) {
        
        NSLog(@"UIControlEventEditingDidEnd");
    }

通过以上方法就能判断组合的状态, 在开发中这个<<意义很大的, 如果多个条件中, 任何一个条件满足我们也可用带<<的枚举给外界组合使用, 就像苹果官方添加<<使用是一样的!

9.Xib相关的一些问题

下图我们可以看出来, 如果通过xib加载出来的view尺寸是不正确的, 在xib中这个view不管你怎么设置都是治标不治本,我们会在layoutSubviews通过自己的宽度来计算子控件的尺寸!

//在这里拿出的宽度是不正确的
- (void)awakeFromNib {}
//对尺寸计算我们一般拿到这个方法中计算(拿到自己宽度计算子控件的尺寸)
- (void)layoutSubviews {
    [super layoutSubviews];
    //在这里拿到自己的宽度是正确的
}
xib.png
那我们也会想到, 如果控制器的view也是xib创建的, 我们该怎么办 ? 其实不管控制器是在哪里创建的, 我们只要只在viewDidLayoutSubviews方法中拿到控制器尺寸来计算子控件尺寸都是正确的, 所以说建议大家以后在viewDidLayoutSubviews计算尺寸:
- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];
    //在这里计算尺寸
}

喜欢的小伙伴请点赞一下吧!如果有不足的地方,请大家及时帮忙纠正与补充,顺便谈谈你的建议!

相关文章

网友评论

  • 烟雨痕:不太建议在layoutSubviews修改frame,如果cell上控件比较多,而且在layoutSubviews修改控件frame,容易造成卡顿,作者可以用instruments测试下。
  • zl520k:#define LRWeakSelf(type) __weak typeof(type) weak##type = type;
    #define LRStrongSelf(type) __strong typeof(type) type = weak##type;
    会造成内存泄漏,不知道你有没有遇到?
    zl520k:@判若两人丶 这个在+function这样函数里面会造成泄漏
    判若两人丶: @zl520k 没遇到过,你在什么场景遇到了
  • 乱尘:很实用的文章
  • C丶丶H:感谢分享
  • b8ac5d26be19:楼主辛苦,学习了。
  • Hardway:貌似在MJ的项目中 都已见到过这些经验
  • zhou天天:接着赞
    判若两人丶: @zhou天天 😄
  • YungFan:是不是说 用viewDidLayoutSubviews替代layoutSubviews 来计算尺寸??
    YungFan:@判若两人丶 立马懂了 谢谢
    判若两人丶: @YungFan 这两含义基本是一样的,但是用的地方不一样,一个是控制器才有的,一个是view,你在控制器里是敲不出来layoutSubviews
  • 405a4e4fb4b0:喜欢
    判若两人丶: @人间一小丑 谢谢支持
  • 卟师:支持楼主,我能转载分享这两篇吗?我会标注上出处和作者的

    卟师:@判若两人丶 :blush:
    卟师:@判若两人丶 微信公众号:iOS面向编码
    判若两人丶: @卟师 可以的,请问转载哪里。
  • 这个昵称就很帅:第一条两层block中weak和strong的配合使用在其他人的代码中也见过,能否解释一下原理?
    23240449cd79:@这个昵称就很帅 这个是为了避免循环引用。如果不这样写,会导致 self 在 strong 引用 self,然后就没得 release 了。
    这个昵称就很帅:👌,我先研究一下,不行就麻烦你再解释一下,我想肯定也有人对这个问题一知半解。
    判若两人丶: @这个昵称就很帅 这个原理不好解释,需要画内存图才能解释清楚的。你可以根据block引用可以自己画图研究下,实在不行我在为你开一篇文章解释这个问题。
  • EdLiya:支持楼主 !
    判若两人丶: @飞扬ZSW 谢谢长官支持。
  • EdLiya:一颗赛艇 !
  • c41bde1f75a4:楼主是好心分享,为什么要用冷嘲热讽对待一个好心的人那?
  • 23240449cd79:辛苦楼主的总结了,但我有个关于第8点的建议。
    不知道楼主是把 NSOptions 和 NSEnum 混淆了,还是没翻译好。其实楼主说的是 NSOptions,我觉得不该写成 “枚举”。NSEnum 才是枚举。他们两个不是同一个东西。在 Swift 中,他们的区别就更大了。
    << 解释为 2的n次方。本质是对的,但不利于初学者的理解。我觉得加个栗子会比较好理解:0010 << 2 == 1000。
    :smile: 大家一起学习。
    23240449cd79:@判若两人丶 另外,刚刚看了下评论,其实大家都对这个点有质疑,说明是有改进的地方的。我本人还是认同 @jarhead @iFire 和 @谈Xx 的观点的。若只是喷,没有说明内容哪里有问题,就直接忽略吧。
    23240449cd79:@判若两人丶 既然说“位运算符号基本都应该懂的吧”,那想来楼主其实是想说的是 NSOptions 咯。既然这样,我觉得不应该解释成 “枚举中如何使用'左移'”,也不应该用“枚举”这个词,容易让初学者混淆。使用方法就那些,差不多都说了。具体原理啥的,我觉得 http://nshipster.com/ns_enum-ns_options/ 解释的比较清楚。
    判若两人丶:@ArthurK 文章中主要是讲在枚举中如何使用'左移',当时在写文章的时候没考虑那么多,因为位运算符号基本都应该懂的吧?不知道你觉得该怎么解决才好
  • brilliance_Liu:干货,有所得,感谢分享
  • R0b1n_L33:看得出来很用心,都是掉坑再出坑的经验之谈 :+1:
  • 6fdb0c58ceca:自认为很有经验, 看了文章很佩服, 不止有我不知道的, 还很佩服博主的总结能力
    判若两人丶: @碧野MAX 我是菜鸟,互相学习!
  • 奔驰小跑:干货 :+1:
  • jarhead:枚举可以赋整型值,你不用枚举一样可以用整型值,把二进制位运算跟枚举强行扯到一起,我只能说楼主你太表面功夫了
    判若两人丶:@jarhead 你要是懂,就给大家解释解释,我也学习学习!别不懂就误导我们大家!
    jarhead:@判若两人丶 请你先读懂我回复文字的逻辑
    判若两人丶:@jarhead 你的意思是枚举没用,你的意思是位运算用在枚举中是扯淡?那你的表面功夫比谁都厉害啊!👍
  • 梦里风吹过:最后一个正常情况下还是使用约束的好.
    判若两人丶: @谷尚科技iOS 你说的很对,就是有时候有可能是xib创建的view有可能是代码创建的(代码当然没问题),所以为了避免问题存在一律放在layout里面拿到自己的尺寸来计算子类的尺寸。反正不管怎样,我们都要根据需求而定。
    梦里风吹过:@判若两人丶 一般情况下确实不需要,使用约束时控件尺寸都是交给约束自行处理的,但是也不排除需要使用尺寸的情况,那种情况下我一般是根据约束情况计算或者直接取. layoutsubview在相关控件fram改变的时候会调用,像你说的,在xib创建的view显示之前,你所取到的是xib的frame,显示出来后因为适配屏幕或者其它情况刷新了frame,之后调用layoutsubview,因此你在layoutsubview函数里取到的是正确的值.通常这是个很有效的方法,比如在不方便使用约束的时候处理横竖屏转换.当然如果非得需要取这个值得话保证在layoutsubview调用之后就好了.
  • Pusswzy:监听textField的改变很有用
  • SuDream:我看了很多遍这个layoutsubview会什么时候掉用。一直都记不住:fearful:
  • SuDream:多发一些这样的经验帖。我每个都收藏了。 还有后面那个xib的方法layoutSubview方法里面计算。layoutsubview这个方法 改了子控件的frame它也会调用又重新计算了
    判若两人丶: @SuDream 只要都在这里面算就可以了。

本文标题:iOS 开发中你是否遇到这些经验问题(二)

本文链接:https://www.haomeiwen.com/subject/zyiesttx.html