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


    前言:

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

    我们都会声明一个弱引用在block中使用, 目的就是防止循环引用, 那么weakSelf与strongSelf一起使用目的是什么呢? 首先先定义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也销毁了, 看起来是没什么区别:

     

    仅仅使用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();

    如果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比如这样设置:

    我们知道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 -

    - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {

    //这里监听文字改变

        return YES;

    }

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

    所以我们要监听UITextField的文字改变不建议使用代理, 我们用addTarget监听文字

    /** 占位文字 */

    @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下载

    https://github.com/luran2358/LRTextView

    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理解就是以自己的左上角为坐标原点, 也就是说Bounds的x值y值是0, 但是Bounds的x值y值有可能是正数也可能是负数, 不一定是0那么Bounds真正是什么意思呢 ?

    • Bounds: 是以自己内容的左上角为坐标原点, 计算出自己的位置和大小

    • Frame: 是以父类内容的左上角为坐标原点, 计算出自己的位置和大小

      那什么是内容呢 ? 首先内容是抽象的, 一个控件不仅仅只有一层矩形框的, 他有很多图层的, 这个内容其实就可以抽象成一个控件的内部图层

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

    上图蓝色和绿色是属于一个控件, 只不过蓝色是控件本身, 绿色是控件的内容, 我们改变这个控件的Bounds的x值y值为-20, 内容位置改变, 控件本身位置不变!

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

     

    上图是一个苹果官方的一个枚举, 我们主要是看的用处, 如果在枚举中只要那它的含义就是可以通过|进行组合使用:

    //随便添加一个UITextField

        UITextField *field = [UITextField new];

        //可以通过 | 组合使用UIControlEventEditingDidBegin, UIControlEventValueChanged,UIControlEventEditingDidEnd

        [field addTarget:self action:@selector(textFieldDidChanged) forControlEvents:UIControlEventEditingDidBegin | UIControlEventValueChanged | UIControlEventEditingDidEnd];

        [self.view addSubview:field];

    如果枚举没有就不能组合使用, 那它有什么规律呢1 :

    //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];

        //在这里计算尺寸

    }

  • 相关阅读:
    终于把老板的项目搞完了---最后一步项目部署
    linux rz/sz 拖动文件上传
    layui之table.render使用(含后台详细代码实现)
    layui upload 后台获取不到值
    Layui upload动态传参,后台接收不到,解决方法
    hibernate 多条件查询,查询部分字段等操作
    IDEA自动生成序列化ID
    MySQL范围查询(日期)
    安全随机数!Java 随机数 Random 与 SecureRandom
    java poi 写excel到指定目录
  • 原文地址:https://www.cnblogs.com/fengmin/p/5801511.html
Copyright © 2020-2023  润新知