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

演示动图
图片1css

图片2

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

分析

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

AutoLayout和Masonry

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

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

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 的布局算法谈性能objective-c

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 Reloadapp

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,从新写一遍

待完善问题:
一、在block外也能够调整布局(已添加)
二、添加控件之间的约束,计划仿造安卓添加一个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;
相关文章
相关标签/搜索