CC_UIAtom实时显示iOS编写UI界面 布局独立文件

演示动图
《CC_UIAtom实时显示iOS编写UI界面 布局独立文件》

《CC_UIAtom实时显示iOS编写UI界面 布局独立文件》

下载地址https://github.com/gwh111/ben…

分析

笔者搜索市面上现有的有名布局框架,先后研究了AutoLayout、Masonry、Flexbox、FlexLib、ClassyLiveLayout、frame等布局框架,最后在Classy和ClassyLiveLayout的基础上改造了一个新布局框架,主要文件由CC_UIAtom、UIView+ClassyExtend和CC_ClassyExtend组成。可在不重新编译的情况下动态修改布局、颜色、文字等属性。现支持view、button、label、textview、textfiled这几个基础视图类。
文章前面分析其他布局方法,后面介绍新框架实现CC_UIAtom。

AutoLayout和Masonry

AutoLayout和Masonry在多视图构建时性能较差,AutoLayout代码量最多,Masonry可读性较强。
AutoLayout的约束是要转换成frame的,这中间的计算过程降低了性能。

Masonry是采用链式DSL(Domain-specific language)来封装NSLayoutConstraint
Domain-specific language
https://en.wikipedia.org/wiki…

Flexbox优化

http://www.cocoachina.com/ios…
FlexBox 的实现 — Yoga
FlexBox是网页布局的一套标准
https://css-tricks.com/snippe…
Flex 布局教程:语法篇
FlexBox 布局模型
Yoga Tutorial: Using a Cross-Platform Layout Engine
从 Auto Layout 的布局算法谈性能

FlexLib

FlexLib是一个通过解析xml文件来创建UI布局的库。把布局和代码分离,很大意义上做到了隔离。减轻编译压力、类似安卓的布局。
https://github.com/zhenglibao…
从代码中剥离开布局,还是需要cmd+r来重启,对于要登录进入很深层级页面的调试麻烦。xml格式需要一些多余的标签,写法容易出错,可借助第三方代码辅助工具来提高布局速度。

Classy

Classy是一个能与UIKit无缝结合stylesheet(样式)系统。它借鉴CSS的思想,但引入新的语法和命名规则。CC_UIAtom主要依赖了这个库。
https://classykit.github.io/C…

ClassyLiveLayout

https://github.com/olegam/Cla…
是一位大神封装的结合Classy、Masonry的布局框架,支持模拟器上的动态布局,我很大程度上借助了他的思路但抛弃了Masonry。
因为无法在真机使用启用Live Reload,仅模拟器支持Live Reload

Live Reload

Live reload can dramatically speed up your development time, with live reload enabled you can instantaneously see your stylesheet changes. Without having to rebuild and navigate back to the same spot within your app.
For more details about these features and more visit the docs or the wiki.

关联对象 AssociatedObject

http://blog.leichunfeng.com/b…
https://www.jianshu.com/p/35d…
有些人觉得这个方法多余,不是很好用。
我们使用了两个方法 objc_getAssociatedObject 以及 objc_setAssociatedObject 来模拟『属性』的存取方法,而使用关联对象模拟实例变量。在CC_UIAtom主要使用了这个技能。

CC_UIAtom介绍

CC_UIAtom、UIView+ClassyExtend和CC_ClassyExtend

CC_UIAtom中封装了控件的类别

/**
 * 控件的类别
 */
typedef enum : NSUInteger {
    CCView,
    CCLabel,
    CCButton,
    CCTextField,
    CCTextView,
} CCAtomType;
+ (id)initAt:(UIView *)view name:(NSString *)name type:(CCAtomType)type finishBlock:(void (^)(id atom))block;

使用了一个方法来构建一个布局控件,通过判断控件类型,初始化一个对应控件。
之后在cas文件中配置对应参数,注意cas文件中控件名和这里初始化的名字要对应不能重复。
通过objc_setAssociatedObject将block传递给UIView,在更新布局后回调出去,才可对布局做后期修改更新(如接口接收到数据后更新布局)。

CC_Button *button=[CC_UIAtom initAt:self.view name:@"MainVC_b_box1" type:CCButton finishBlock:^(CC_Button *atom) {
    [atom setBackgroundColor:[UIColor brownColor]];
    [atom addTappedOnceDelay:.1 withBlock:^(UIButton *button) {
        CCLOG(@"tapped");
    }];
}];

普通创建和用CC_UIAtom创建实例对比

//普通创建
UILabel *l=[[UILabel alloc]initWithFrame:CGRectMake(250, 100, 100, 40)];
l.backgroundColor=[UIColor yellowColor];
l.text=@"普通label";
[self.view addSubview:l];
    
//CC_UIAtom创建
[CC_UIAtom initAt:self.view name:@"MainVC_i_figure1" class:[UIImageView class] finishBlock:^(UIImageView *atom) {

}];
    
[CC_UIAtom initAt:self.view name:@"MainVC_b_box1" class:[CC_Button class] finishBlock:^(CC_Button *atom) {
    //更多代码
    [atom setBackgroundColor:[UIColor brownColor]];
    [atom addTappedOnceDelay:.1 withBlock:^(UIButton *button) {
            
        [self requestxxx1];
    }];
}];

模块组成

UIView+ClassyExtend

是一个扩展文件,在里面实现了动态读取更新的cas文件,并修改控件属性。参考了ClassyLiveLayout。
对于修改后的cas文件,因为在启动时有监听,会通过Live Reload通知,然后通过objc_setAssociatedObject和objc_getAssociatedObject更新属性,实现不编译修改。更新完会实现回调,为了处理动态修改,比如页面接收到数据后才调整UI,就需要在回调里修改布局,否则会被覆盖。

CC_ClassyExtend

一个解析布局的文件,主要任务是解析布局为了给真机使用。
建议所有cas文件放在二级目录内,个人喜好按控件分文件夹。

总结一下整个流程

启动后读取stylesheet.cas的文件,解析里面引用的文件路径,读取这些文件中的控件属性,把它们合并,创建一个配置文件放入缓存。在实际使用中,无论真机和模拟器,都会读取配置文件中的配置并构建,模拟器是直接读取文件,真机是读取生成的文件。
建议stylesheet.cas只添加cas文件路径,将所有布局写到单独模块目录下。

用pods安装

platform :ios, '8.0'
inhibit_all_warnings!

target 'xxx' do
  pod 'bench_ios'
end

Classy合并的警告

CASUtilities.h
this function declaration is not a prototype
解决:添加void
UIColor+CASAdditions.m:18:37: warning: format specifies type ‘unsigned int ‘ but the argument has type ‘NSUInteger ‘ (aka ‘unsigned long *’)
解决使用int “%d”, &result
UIColor+CASAdditions.m:85:53: warning: values of type ‘NSUInteger’ should not be used as format arguments; add an explicit cast to ‘unsigned long’ instead
解决[hex stringByAppendingFormat:@”%02lx”, (unsigned long)(CGColorGetAlpha(self.CGColor) * 255.0f)]
Classy.h:25:9: warning: non-portable path to file ‘”UIToolbar+CASAdditions.h”’
解决:把名字复制一遍,删掉import,重新写一遍

待完善问题:
1、在block外也可以调整布局(已添加)
2、添加控件之间的约束,计划仿造安卓添加一个toRightOf(已添加)

添加约束属性列表

/**
 *  控件宽
 *  控件高
 *  控件宽和指定控件id的宽相等
 *  控件高和指定控件id的高相等
 *  控件宽和父视图宽相等 如比父视图少5 填“-5” 大10 填“10” 相等 填“0”
 *  控件高和父视图高相等
 *  控件宽和屏幕宽相等 如比父视图少5 填“-5” 大10 填“10” 相等 填“0”
 *  控件高和屏幕高相等
 */
@property(nonatomic, assign) CGFloat cas_width;
@property(nonatomic, assign) CGFloat cas_height;
@property(nonatomic, retain) NSString *cas_widthSameAs;
@property(nonatomic, retain) NSString *cas_heightSameAs;
@property(nonatomic, retain) NSString *cas_widthSameAsParent;
@property(nonatomic, retain) NSString *cas_heightSameAsParent;
@property(nonatomic, retain) NSString *cas_widthSameAsScreen;
@property(nonatomic, retain) NSString *cas_heightSameAsScreen;

/**
 *  上偏移的值
 *  下偏移的值
 *  左偏移的值
 *  右偏移的值
 */
@property(nonatomic, assign) CGFloat cas_marginTop;
@property(nonatomic, assign) CGFloat cas_marginBottom;
@property(nonatomic, assign) CGFloat cas_marginLeft;
@property(nonatomic, assign) CGFloat cas_marginRight;

/**
 *  背景色
 *  背景图
 *  文字
 *  字体颜色
 *  字体大小
 */
@property(nonatomic, retain) NSString *cas_backgroundColor;
@property(nonatomic, retain) NSString *cas_backgroundImage;
@property(nonatomic, retain) NSString *cas_text;
@property(nonatomic, retain) NSString *cas_textColor;
@property(nonatomic, assign) int cas_font;

/**
 *  控件ID为自定义的控件名
 *  将该控件的底部置于给定ID的控件之上;
 *  将该控件的底部置于给定ID的控件之下;
 *  将该控件的右边缘与给定ID的控件左边缘对齐;
 *  将该控件的左边缘与给定ID的控件右边缘对齐;
 */
@property(nonatomic, retain) NSString *cas_above;
@property(nonatomic, retain) NSString *cas_below;
@property(nonatomic, retain) NSString *cas_toRightOf;
@property(nonatomic, retain) NSString *cas_toLeftOf;

/**
 *  设为@“1”即可
 *  将该控件的顶部边缘与给定ID的顶部边缘对齐;
 *  将该控件的底部边缘与给定ID的底部边缘对齐;
 *  将该控件的左边缘与给定ID的左边缘对齐;
 *  将该控件的右边缘与给定ID的右边缘对齐;
 */
@property(nonatomic, retain) NSString *cas_alignTop;
@property(nonatomic, retain) NSString *cas_alignBottom;
@property(nonatomic, retain) NSString *cas_alignLeft;
@property(nonatomic, retain) NSString *cas_alignRight;

/**
 *  设为@“1”即可
 *  如果为true,将该控件的顶部与其父控件的顶部对齐;
 *  如果为true,将该控件的底部与其父控件的底部对齐;
 *  如果为true,将该控件的左部与其父控件的左部对齐;
 *  如果为true,将该控件的右部与其父控件的右部对齐;
 */
@property(nonatomic, retain) NSString *cas_alignParentTop;
@property(nonatomic, retain) NSString *cas_alignParentBottom;
@property(nonatomic, retain) NSString *cas_alignParentLeft;
@property(nonatomic, retain) NSString *cas_alignParentRight;
    原文作者:尾巴
    原文地址: https://segmentfault.com/a/1190000016223095
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞