zoukankan      html  css  js  c++  java
  • iOS-Storyboad动态刷新

    iOS-Storyboad动态刷新

    什么叫做Storyboard动态刷新

    在项目开发中,如果可以在xib(storyboard)中,动态显示运行效果图,那么实在是太爽了。在Xcode 6之后就为我们提供了这样的一种方式,来实现这样的效果,通过IBInspectable和IB_DESIGNABLE 来实现。

    一图胜千言,请看效果图:
    这里写图片描述

    IB_DESIGNABLE-属性介绍

    IB_DESIGNABLE的宏的功能就是让XCode动态渲染出该类图形化界面。需要注意的是,自定义类必须是UIView的子类。

    使用方式,把该宏加在自定义类的前面:

    #import <UIKit/UIKit.h>
    
    IB_DESIGNABLE
    @interface DynamicView1 : UIView
    
    @end

    现在可以将DynamicView1类实例,显示是xib中了。

    具体方法为:

        1.  随便创建一个xib(storyboard)文件
        2.  将一个UIView控件拖动到xib(storyboard)中
        3.  将这个UIView控件设置为刚刚创建的类(DynamicView1)

    IBInspectable-属性介绍

    让支持KVC的属性能够在Attribute Inspector中配置。

    但是现在不一样了。在xcode6之后,为我们提供了宏定义IBInspectable,在需要的属性之前定义这个宏定义,我们就可以在图形化界面中设置这个数值了。

    使用方式,把该宏加在自定义类的前面:
    在声明文件中添加:

    @property (strong, nonatomic) IBInspectable NSString *string;

    在IB中设置属性,会调用响应的set方法。因此,如果你想在设置这个数值的时候,进行一些其他操作。那么你可以重写赋值方法:

    - (void)setString:(NSString){
        _string = string;
        //其他操作
    }

    可以显示的数据如下:

    Int
    CGFloat
    Double
    String
    Bool
    CGPoint
    CGSize
    CGRect
    UIColor
    UIImage

    其实 @IBInspectable 并没有做什么太神奇的事情,我们如果查看 IB 中这个 view 的 Identity Inspector 的话会看到刚才所设定的颜色值被作为了 Runtime Attribute 被使用了。其实手动直接在 Runtime Attributes 中设定颜色也有同样的效果,因此 @IBInspectable 唯一做的事情就是在 IB 面板上为我们提供了一个很方便地修改属性的入口,并没有其他太多神奇之处。

    通过IB_DESIGNABLE配合IBInspectable可以实现动态刷新

    IB_DESIGNABLE作用为:动态渲染图像到xib(storyboard)中。
    IBInspectable作用:使属性可以在IB界面中可视化编辑设置。
    结合两者,我们就能实现动态刷新界面。

    使用方法:
    在声明文件中:

    IB_DESIGNABLE
    @interface DynamicView1 : UIView
    
    @property (strong, nonatomic) UIImageView *imageView;
    @property (strong, nonatomic) UILabel *label;
    
    @property (strong, nonatomic) IBInspectable NSString *string;
    @property (strong, nonatomic) IBInspectable UIImage *image;
    @property (assign, nonatomic) IBInspectable CGFloat cornerRadius;
    @property (assign, nonatomic) IBInspectable CGFloat borderWidth;
    @property (strong, nonatomic) IBInspectable UIColor *borderColor;
    
    @end

    在实现文件中:

    #import "DynamicView1.h"
    
    @implementation DynamicView1
    - (void)prepareForInterfaceBuilder{
        self.backgroundColor = [UIColor blackColor];
    }
    
    - (instancetype)initWithCoder:(NSCoder *)aDecoder{
        self = [super initWithCoder:aDecoder];
        if (self) {
            //custom initlizaiton
            self.opaque = NO;
            self.clearsContextBeforeDrawing = YES;
        }
        return self;
    }
    
    // Only override drawRect: if you perform custom drawing.
    // An empty implementation adversely affects performance during animation.
    - (void)drawRect:(CGRect)rect {
        self.layer.masksToBounds = YES;
        //图片
        if (self.image) {
            self.imageView = [[UIImageView alloc]init];
            self.imageView.frame = rect;
            self.imageView.contentMode = UIViewContentModeScaleAspectFit;
            self.imageView.image = self.image;
            [self addSubview:self.imageView];
        }
        //文字
        if (self.string) {
            self.label = [[UILabel alloc]initWithFrame:rect];
            self.label.backgroundColor = [UIColor clearColor];
            self.label.textAlignment = NSTextAlignmentCenter;
            self.label.text = self.string;
            [self addSubview:self.label];
        }
    }
    
    - (void)setCornerRadius:(CGFloat)cornerRadius{
        _cornerRadius = cornerRadius;
        self.layer.cornerRadius = cornerRadius;
    }
    
    - (void)setBorderWidth:(CGFloat)borderWidth{
        _borderWidth = borderWidth;
        self.layer.borderWidth = borderWidth;
    }
    
    - (void)setBorderColor:(UIColor *)borderColor{
        _borderColor = borderColor;
        self.layer.borderColor = borderColor.CGColor;
    }
    
    @end

    之后,我们可以在xib(storyboard)中随便拖一个view进来。将这个view设置为自定义属性。图像就会自动渲染出来了。(如果不行,勾选editor-automitically refresh views自动加载图像)

    上面的可以复制粘贴使用,效果为一开始展示的图片。
    这里写图片描述

    IB中动态显示方法介绍

    prepareForInterfaceBuilder方法

    每次在 IB 即将把这个自定义的 view 渲染到画布之前会调用这个方法进行最后的配置。但是这个方法在app运行的时候,是不调用的。

    使用方法:

    - (void)prepareForInterfaceBuilder{
        self.backgroundColor = [UIColor blackColor];
    }

    效果图:
    左边为app运行时, 右边为xib中渲染效果
    这里写图片描述

    TARGET_INTERFACE_BUILDER宏定义

    通过宏定义,可以在控制IB中渲染的视图与app中运行的视图。

    #if !TARGET_INTERFACE_BUILDER
       // this code will run in the app itself
       //此处编译的代码,在app运行调用
    #else
       // this code will execute only in IB
       //此处编译的代码,在IB中绘画中调用
    #endif
    }

    使用方法:

    - (void)drawRect:(CGRect)rect {
        self.layer.masksToBounds = YES;
    
    #if !TARGET_INTERFACE_BUILDER
        // this code will run in the app itself
        //图片
        if (self.image) {
            self.imageView = [[UIImageView alloc]init];
            self.imageView.frame = rect;
            self.imageView.contentMode = UIViewContentModeScaleAspectFit;
            self.imageView.image = self.image;
            [self addSubview:self.imageView];
        }
        //文字
        if (self.string) {
            self.label = [[UILabel alloc]initWithFrame:rect];
            self.label.backgroundColor = [UIColor clearColor];
            self.label.textAlignment = NSTextAlignmentCenter;
            self.label.text = self.string;
            [self addSubview:self.label];
        }
    #else
        // this code will execute only in IB
        //文字
        if (self.string) {
            self.label = [[UILabel alloc]initWithFrame:rect];
            self.label.backgroundColor = [UIColor clearColor];
            self.label.textAlignment = NSTextAlignmentCenter;
            self.label.text = self.string;
            [self addSubview:self.label];
        }
    #endif
    }

    效果图:
    左边为app运行时, 右边为xib中渲染效果
    这里写图片描述

    自定义渲染 view 的调试

    对于简单的自定义 view 来说,实时显示和属性设定什么的并不是一件很难的事情。但是对于那些比较复杂的 view,如果我们遇到某些渲染上的问题的话,如果只能靠猜的话,就未免太可怜了。幸好,Apple 为 view 在 IB 中的渲染的调试也提供了相应的方法。在 view 的源代码中设置好断点,然后切到 IB,点选中我们的自定义的 view 后,我们就可以使用菜单里的 Editor -> Debug Selected Views 来让 IB 对这个自定义 view 进行渲染。如果触发了代码中的断点,那我们的代码就会被暂停在断点处,lldb 也会就位听我们调遣。一切都感觉良好,不是么?
    这里写图片描述

    动态刷新,加载资源文件出错(xib, image)

    问题一描述

    1. 创建UIView子类,自定义界面
    2. 在自定义界面上添加collectionView界面
    3. 通过xib文件,编写collectionViewCell文件
    4. 为collectionView注册cell
    5. 在delegate方法中设置cell(出现问题, cell无法被加载, 界面直接crash)

    出错代码

    [self.collectionView registerNib:[UINib nibWithNibName:@"ICDateCollectionViewCell" 
            bundle:nil] forCellWithReuseIdentifier:@"cell"];

    出错原因:
    IB 使用的 bundle 和 app 运行时的 mainBundle 不是一个概念,我们需要在设计时的 IB 的 bundle 可以通过在自定义的 view 自身的 bundle 中进行查找并使用([NSBundle bundleForClass:[self class]]])。

    为了验证IB使用的bundle和app运行的时的mainBundle的区别,下面是我们验证:
    在IB中debug调试状态下,打印[NSBundle bundleForClass:[self class]]][NSBundle mainBundle]显示如下
    这里写图片描述

    在app运行状态下,打印[NSBundle bundleForClass:[self class]]][NSBundle mainBundle]显示如下,他们都指向同一个文件目录
    这里写图片描述

    因此,在不同的运行状况下,app的运行状态是不同的,如果设置错了bundle(文件目录),那么将无法找到我们需要的文件。(cell的xib文件就是一个例子)。

    解决方法
    将加载的xib文件的目录设置为当前view的class,这样就可以加载到xib文件了。

    [self.collectionView registerNib:[UINib nibWithNibName:@"ICDateCollectionViewCell" bundle:
                                          [NSBundle bundleForClass:[self class]]] forCellWithReuseIdentifier:@"cell"];

    问题二描述

    1. 获取图像a对应的两倍图,三倍图
    2. 将图片保存在xcode的图片文件夹(Assets.xcassets)中
    3. 创建UIView子类,自定义界面
    4. 在自定义界面上添加一个UIImageView
    5. 获取一个UIImage,将UIImage添加到UIImageView上(出现问题,UIImage无法获取)
    6. 在IB中无法动态渲染出相应界面

    出错代码

    UIImage *image = [UIImage imageNamed:@"C"];
    self.imageView.image = image;

    出错原因:
    在上一个问题中已经说明:IB 使用的 bundle 和 app 运行时的 mainBundle 不是一个概念。

    调用imageNamed:方法,自动在mainBundle中寻找图片。因此不可用。如果将获取图片代码改为:

    NSBundle *bundle = [NSBundle bundleForClass:self.class];
    NSString *path = [bundle pathForResource:@"C" ofType:@"png"];
    UIImage *image = [[UIImage alloc]initWithData:[NSData dataWithContentsOfFile:path]];

    那么是不是可以解决这个问题了呢?

    还是不可以。 因为,图片文件夹(Assets.xcassets)中的图片,默认只可以通过imageNamed:方法获取。

    解决方法
    将图片存放在项目根目录下。

    然后在调用之前代码:

    //NSBundle其实就是一个树形结构的根目录
    NSBundle *bundle = [NSBundle bundleForClass:self.class];
    NSString *path = [bundle pathForResource:@"C" ofType:@"png"];
    UIImage *image = [[UIImage alloc]initWithData:[NSData dataWithContentsOfFile:path]];

    问题解决参考
    xcode 6 IB_DESIGNABLE- not loading resources from bundle in Interface builder
    IBDesignable crashes when attempting to instantiate a nib

    下面是写的一个demo

    这里写图片描述

    下载地址:
    http://download.csdn.net/detail/daiyibo123/9193951

    参考

    iOS SDK详解之IBInspectable和IB_DESIGNABLE-Storyboad动态刷新

    WWDC 2014 Session笔记 - 可视化开发,IB 的新时代

  • 相关阅读:
    Demo
    Demo
    z-yelir-~
    CSP考前总结
    NOIP刷题
    清北学堂
    qsing
    【csp模拟赛九】--dfs3
    【csp模拟赛九】--dfs2
    【csp模拟赛九】--dfs
  • 原文地址:https://www.cnblogs.com/AbeDay/p/5026864.html
Copyright © 2011-2022 走看看