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 的新时代

  • 相关阅读:
    ECharts之柱状图 饼状图 折线图
    Vue自定义指令(directive)
    HDU 1231 最大连续子序列
    POJ 2533 Longest Ordered Subsequence
    HDU 1163 Eddy's digital Roots
    HDU 2317 Nasty Hacks
    HDU 2571 命运
    HDU 4224 Enumeration?
    HDU 1257 最少拦截系统
    HDU 2740 Root of the Problem
  • 原文地址:https://www.cnblogs.com/AbeDay/p/5026864.html
Copyright © 2011-2022 走看看