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

前言:

以前在掘金社区是没有原做权限的, 因此这篇文章在以前是经过连接分享的, 如今有原做权限因此为了方便从新上传一下文章!在上一篇文章中我相信帮助了不少的小伙伴, 那么在这篇文章但愿还能帮助到你!ios

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

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

#define LRWeakSelf(type)  __weak typeof(type) weak##type = type;
#define LRStrongSelf(type)  __strong typeof(type) type = weak##type;复制代码

咱们建立一个shop而且在shop.myBlock代码块中使用弱引用LRWeakSelf(shop);github

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

LRWeakSelf(shop);LRStrongSelf(shop);一块儿使用api

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也销毁了, 看起来是没什么区别:app

仅仅使用LRWeakSelf(shop);而且在myBlock中增长一个延迟3秒在输出就会出现问题, 虽然对象销毁了, 输出的值倒是nullide

//弱引用
 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();复制代码

若是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);一块儿使用的好处, 不但能打印出我想要的值,并且也不会形成循环引用 , 在开发中这两个方法能够根据实际状况进行使用!ui

2.使用UIAppearance注意的问题

若是不熟悉能够点击了解, UIAppearance它的目的就是设置全局显示样式, 咱们知道只要带UI_APPEARANCE_SELECTOR这个宏, 咱们就可使用UIAppearance好比这样设置:atom

咱们知道UIBarButtonItem它是有状态的好比UIControlStateNormal或者是UIControlStateDisabled状态 若是经过UIAppearance设置UIControlStateDisabled状态下的颜色是很差使的, 由于使用appearance会有一些延迟, 致使在不一样状态下的颜色很差使, 咱们只要强制刷新一下就能够了:spa

// 刷新
[self.navigationController.navigationBar layoutIfNeeded];复制代码

因此之后使用UIAppearance在某个状态下设置颜色,字体等很差使, 只须要在对应的位置用layoutIfNeeded刷新一下就能够了!

3. UITextField使用注意

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

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

你们监听UITextField文字的改变会用到代理:

#pragma mark - 
  
  
  
  

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

 复制代码

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

因此咱们要监听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能够直接使用: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那么系统自带的侧滑返回功能就很差使了!

//设置代理
self.interactivePopGestureRecognizer.delegate = self;
#pragma mark - 
  
  
  
  

 
  
  //实现代理方法:return YES :手势有效, NO :手势无效 - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer { //当导航控制器的子控制器个数 大于1 手势才有效 return self.childViewControllers.count > 1; } 

 复制代码

7.从新认识Bounds

咱们以前对Bounds理解就是以本身的左上角为坐标原点, 也就是说Boundsxy值是0, 可是Boundsxy值有多是正数也多是负数, 不必定是0那么Bounds真正是什么意思呢 ?

  • Bounds: 是以本身内容的左上角为坐标原点, 计算出本身的位置和大小
  • Frame: 是以父类内容的左上角为坐标原点, 计算出本身的位置和大小 那什么是内容呢 ? 首先内容是抽象的, 一个控件不只仅只有一层矩形框的, 他有不少图层的, 这个内容其实就能够抽象成一个控件的内部图层

  • 内容:就是内部的东西, 它的子控件也属于内容,也就是说修改了Buonds子控件的位置也会跟着改变

上图蓝色和绿色是属于一个控件, 只不过蓝色是控件自己, 绿色是控件的内容, 咱们改变这个控件的Boundsxy值为-20, 内容位置改变, 控件自己位置不变!

8.跟枚举相关的一些符号的含义

上图是一个苹果官方的一个枚举, 咱们主要是看<<的用处(它是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];
    //在这里拿到本身的宽度是正确的
}复制代码

那咱们也会想到, 若是控制器的view也是xib建立的, 咱们该怎么办 ? 其实无论控制器是在哪里建立的, 咱们只要只在viewDidLayoutSubviews方法中拿到控制器尺寸来计算子控件尺寸都是正确的, 因此说建议你们之后在viewDidLayoutSubviews计算尺寸:

- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];
    //在这里计算尺寸
}复制代码

喜欢的小伙伴请点赞一下吧!若是有不足的地方,请你们及时帮忙纠正与补充,顺便谈谈你的建议! 原文阅读

相关文章
相关标签/搜索