高效的iOS布局框架

介绍

MKYogaLayout是一个布局工具, 布局引擎是Facebook开发的Yoga跨平台布局框架.
使用该框架进行iOS上的布局搭建,能够具备高效和维护方便等特点

为什么要开发这个?

  • 苹果提供的StoryBoard的布局方式相信绝大多数团队都是放弃的。
  • AutoLayout系列虽能解决大部分痛点,但是还是不够高效。
  • frame(扩展工具),适配性和灵活性也欠佳。

该框架的特点

  • 声明式:布局逻辑和业务逻辑完全分离
  • FlexBox:盒模型,能够轻松搭建和适配各类UI
  • JSON:支持编码式和文件式,可轻松编码和远程下发
  • 样式表:全局+局部,可抽离UIKit, 减少布局代码
  • View注入:可对布局View进行二次操作
  • 扩展视图属性:将各种视图属性进行统一
  • 事件:支持所有视图点击态,点击事件
  • 图片代理:接入项目中用的图片加载器
  • 路由代理:接入项目中的路由框架
  • ...

GitHub传送门

集成方式

// pods

使用

基本使用

/// 定义Layout结构
NSDictionary *layout = @{
        kLView: UIView.class, // 指定视图类型
        kLBg: @"#00FF00",     // 视图属性
        kLPadding: @20,       // 视图布局属性
        kLSubviews: @[        // 子视图
                @{
                        kLView: UILabel.class,
                        kLAlias: @"userName", // 视图注入别名
                        kLStyle: @"button1",  // 样式名称
                        kLText: @"{{name}}",  // 数据绑定
                }
        ]
};
// 在 self.view 内创建视图
[UIView createSubViewsByLayout:layout rootView:self.view context:self style:nil data:nil];

// 上述代码将在self.view中创建一个Label, 并且设置self.view的背景色和内边距
  • layout: 布局结构,字典类型
  • rootView: 根视图
  • context: 所属上下文,视图注入和点击事件都将已改实例为载体
  • style: 局部样式表
  • model: 绑定的数据

样式表

将同样的属性进行先声明,然后使用的时候指定样式名称即可;
样式表可以统一多个页面或整个App的UI效果,也可让布局代码更加的简短,方便后期进行整体修改。
样式表分为局部样式表和全局样式表。

全局样式表声明

// Key: 值为样式名称
// Value: 样式集合
NSDictionary *styles = @{  
        @"item": @{
                kLBg: @"#eeeeee",
                kLBg_Highlight: @"#999999",
                kLPaddingVertical: @30,
                kLAlignItems: @(YGAlignCenter),
                kLTextAlignment: @(NSTextAlignmentCenter),
        },
        @"item2": @{
               ...
        }
};

[MKYoga registerStyle:styles];

样式表使用

NSDictionary *layout = @{
        ...
        kLSubviews: @[        // 子视图
                @{
                        kLView: UILabel.class,
                        kLStyle: @"item ",  //  指定单个样式
                },
                @{
                        kLView: UILabel.class,
                        kLStyle: @"item item2",  // 指定多个样式
                }
        ]
};

局部样式表

/// 声明
NSDictionary *mPageStyles = @{
        @"nickname": @{
            ...
        }
};

NSDictionary *layout = @{
        ...
        kLSubviews: @[
                @{
                        kLView: UILabel.class,
                        kLStyle: @"nickname", // 指定局部样式表名称
                },
                @{
                        kLView: UILabel.class,
                        kLStyle: @"item",  //  指定单个样式
                },
                @{
                        kLView: UILabel.class,
                        kLStyle: @"item item2 nickname",  // 混合指定多个样式
                }]
};

[UIView createSubViewsByLayout:layout rootView:self.view context:self style:mPageStyles data:nil];

样式表生效优先级

若指定了多个样式表,且多个样式表存在同样属性的设置,此时优先级规则如下:

布局内样式 > kLStyle 声明的样式表;
若kLStyle是多个样式,则靠后的样式覆盖前面样式。

View注入

使用MKYogaLayout时,子视图是不需要我们初始化的,但很多业务还是会用到子视图,就可以使用视图注入的方式进行操作

@interface ViewController ()
@property (nonatomic, strong) UILabel *nickname;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    NSDictionary *layout = @{
            ...
            kLSubviews: @[
                    @{
                            kLView: UILabel.class,
                            kLAlias: @"nickname", // 将这个UILabel 注入到 self.nickname 内
                            ...
                    },
                    ...
            ]
    };
    [UIView createSubViewsByLayout:layout rootView:self.view context:self style:nil data:nil];

    // some code
    self.nickname.hidden = YES;
}
  • kLAlias: 注入的属性名

注入时,一定要传入context对象

事件

使用MKYogaLayout时,所有视图都将具有点击事件

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    NSDictionary *layout = @{
            ...
            kLSubviews: @[
                    @{
                            kLView: UILabel.class,
                            kLBindTap: @"gotoUserPage" // 绑定事件名,这里没有 ":"
                            ...
                    },
                    ...
            ]
    };
    [UIView createSubViewsByLayout:layout rootView:self.view context:self style:nil data:nil];
}

// 事件执行, sender 为事件发送的视图
- (void)gotoUserPage:(id)sender {
    // some code
}

@end
  • 默认点击态:是要使用 kLBindTap, 默认点击态是 视图透明度点击时降低 50%
  • 自定义点击态:可使用 kLBg_Highlight 修改点击背景的高亮色
  • kLBindTouch: 没有点击态的轻触事件

绑定事件时,一定要传入context对象

图片代理 & 路由代理

如果您的App使用的第三方的图片加载器/路由框架,可以在初始化MKYoga的时候,设置代理,进行图片加载,该代理为全局代理。


@interface AppDelegate () <MKYogaDelegate> // 代理
@end

@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    ...
    [[MKYoga shareInstance] setDelegate:self];
    return YES;
}

// value: 为配置的图片地址
// style: 为该视图的样式集合
- (void)mkYogaNetworkImageLoader:(UIView *)view value:(NSString *)value style:(NSDictionary *)style {
    // 进行图片加载
}

- (void)mkYogaRouterEvent:(UIView *)view value:(NSURL *)value {
    // 进行路由跳转
}
@{
        kLView: UIImageView.class,
        kLUri : @"app://module/fun1?xx1=1", // 配置路由
        kLImagePath: @"https://xxxxx.jpg",   // 设置图片地址
        ...
}
  • kLUri: 配置了,默认具有点击事件,点击后,将执行路由代理
  • kLImagePath: 配置图片地址,加载图片时,进入图片代理

视图附加对象

有些业务开发时,多个视图会配置同一个点击事件,但是由于视图不一样,会做不同的处理,此时可以给视图绑定附加对象

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    NSDictionary *layout = @{
            ...
            kLSubviews: @[
                    @{
                            kLView: UILabel.class,
                            kLBindTap: @"gotoUserPage",
                            kLObj: @"101"
                            ...
                    },
                    @{
                            kLView: UILabel.class,
                            kLBindTap: @"gotoUserPage"
                            kLObj: @"102"
                            ...
                    },
                    ...
            ]
    };
    [UIView createSubViewsByLayout:layout rootView:self.view context:self style:nil data:nil];
}

// 事件执行, sender 为事件发送的视图
- (void)gotoUserPage:(id)sender {
    NSString *uId = sender.view.obj;
    // do some biz
}

@end

数据绑定

使用{{ expression }},用以区分是普通文字还是表达式。

NSDictionary *layout = @{
        ...
        kLSubviews: @[
                @{
                        kLView: UILabel.class,
                        kLText: @"{{title}}",
                        kLMarginLeft: @20,
                },
                @{
                        kLView: UILabel.class,
                        kLText: @"{{age}}",
                        kLMarginLeft: @20,
                },
                @{
                        kLView: UILabel.class,
                        kLText: @"{{info.school.name}}",
                        kLMarginLeft: @20,
                }
        ]
};

model = [MPDataModel new];
model.title = @"Milker";
model.age = 12;
model.info = @{
        @"school": @{
                @"name": @"MIT",
                @"address": @"USA"
        }
};
[UIView createSubViewsByLayout:layout rootView:self.view context:self style:nil data:model];

常用表达式:

  • {{ gender == 1 ? '男' : '女' }}
  • 取值采用ONGL机制: a.b.c.x 可以串联字典或 model.

视图更新:

model.title = @"Milker"; = @"Jack";
[self.view updateViewWithData:model];

常用属性说明

FlexBox布局属性

通用属性

  • kLCorner: 圆角, 支持单圆角设置,逻辑尺寸
  • kLBorder: 边框, "WidthColor"
  • kLShadow: 阴影,

UIView

  • kLView: 字符串/class, 指定视图类名或类型
  • kLSubviews: 数组,子视图集合
  • kLStyle: 样式名,多个样式用 空格 分开
  • kLUri: 字符串,路由值
  • kLBindTap: 字符串,函数名,点击事件
  • kLBindTouch: 字符串,函数名,Touch事件
  • kLAlias: 字符串,注入视图的别名
  • kLObj: 任何,视图的附加对象

UILabel

  • kLFont: 普通字体
  • kLBoldFont: 加粗字体
  • kLMaxLines: 最大行数
  • kLTextAlignment: 文本对齐
  • ...

UIButton

  • kLTitle: 文字标题
  • kLBg_Highlight:高亮背景色
  • kLBg_Disabled: 不可用时背景色
  • ...

UIImageView

  • kLImagePath: 本地图片、网络图片,路径或url
  • kLImageName: 工程图标
  • kLContentMode: 图片裁剪方式
  • kLPlaceHolder: 占位图

UIScrollView

由于UIScrollView的特殊性,封装了一层,可以方便使用

  • kLFlexDirection: 置顶横向或纵向的滚动方式
  • kLBouncesEnabled: 置顶是否有弹性效果
  • kLPagingEnabled,kLShowsHorizontalScrollIndicator,kLShowsVerticalScrollIndicator 等

...

很简单的效果

image.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,907评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,987评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,298评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,586评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,633评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,488评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,275评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,176评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,619评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,819评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,932评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,655评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,265评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,871评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,994评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,095评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,884评论 2 354

推荐阅读更多精彩内容

  • 翻译自“Collection View Programming Guide for iOS” 0 关于iOS集合视...
    lakerszhy阅读 3,864评论 1 22
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,101评论 1 32
  • 1、感恩自己 在以往的口音基础上,学习美音发音。在没有完整讲解的情况下,通过反复听原句,理解了口语素材里的重读、连...
    阿德doris阅读 130评论 0 0
  • 一大早就被体育部给耍了,不做早操,很后面才通知,我都已经起来刷了个牙了,然后我又回去睡了回笼觉了。 接下来上课,我...
    也许可能大概吧阅读 114评论 0 0
  • 今天无意中见到这样一句话:读书不知不觉受熏染,脸上自然透露出一股清醇爽朗之气,同时在谈吐上也自然高远不...
    云雨眠阅读 361评论 1 2