iOS快速开发框架Bee-Framework应用和解析 (四,完结)UI容器,UICell, UILayout(XML + CSS)

        作为Bee系列解析的最后一个部分,本文分享一下Bee的UI开发。Bee的UI开发很有特点:

  •         BeeUI的核心是XML布局和风格。支持CSS语法,支持额外的布局优化,支持更统一方便的UISignal事件。感觉吸收了QT和JS,CSS各自UI开发的长处(布局,事件,CSS标签)。
  •         BeeUI的内部还是通过UIView的层级和frame的设定实现自动布局,支持原生自定义控件。。
  •         Bee在UI容器中引入了UILayout树,每个节点是一个BeeUILayout对象,附着在对应的UIView对象上管理布局和风格。
  •         Bee的UI开发混合了多种UI开发的模式,可以非常灵活地组合,但得心应手地掌握取舍需要时间和学习成本。
       本文试图对比开发手册(代码目录中./document/developer_manual)和XML模板说明( https://github.com/gavinkwoe/BeeFramework/wiki/Bee-Templates-Manual),对BeeUI的实现原理,和用途做更深入的讲解,再举一些例子来说明自动布局的高级用法。来一些问题做为提纲:
  •         Bee有哪些UI容器,XML template和原生xib, storyboard相比,各有哪些优缺点和适用的场合?
  •         Bee有哪些UI控件,有何特点和适用场合?
  •         BeeUILayout是怎么实现自动布局的?有自定义布局需求的时候,该怎么去做?需要算UIView的frame吗?能使用原生基于Constraint的自动布局吗?如何选择?
  •         如何快速掌握XML Template的书写和调试?
        A1: 在前文提到BeeUIRouter和BeeUIBoard继承自ViewController, 区别是BeeUIRouter用于rootViewController, BeeUIStack封装了UINavigationController。一般BeeUIRouter是单件,管理若干BeeUIStack, BeeUIStack管理若干BeeUIBoard, 而BeeUIBoard就是页面。代码的使用比较简单,可以参考开发者手册。优缺点和使用场合见下表:
        

 

优点

缺点

备注

StoryBoard

直观

不利于多人协同

 

Xib

适用广泛

Delegate/source/bind/Auto size复杂,维护代价稍高

也可以选择BeeUIBoard+Xib

Bee XML容器

直观,维护容易

没有原生功能丰富,没有Constraint auto size等。

也可以支持自定义控件


        那么,我究竟何时该使用XML模板,何时使用自定义控件? 除了支持遗留的自定义控件代码,个人认为XML模板提供的核心功能是布局/风格描述和自动布局。如果某个界面你不需要自动布局,那么就不要写XML,在代码里直接去写布局。比如浏览器界面,或者即时通信的泡泡消息界面等。

        A2:BeeUI的页面上的控件主要是BeeUICell类, 继承自UIView,开发者可以用这个来实现自定义的格子控件。Bee也封装了大多数的常用原生控件,具体支持的类型可见Bee_UITemplateParserXML.m的load函数。可见Bee没有封装TableView, 一般使用BeeUIScrollView和BeeUICell代替。Bee的控件封装优点是引入了自动布局,CSS风格,使用简洁。如果需要使用TableView, CoreText, PickerView, Mapkit, GLkit, SceneKit View的时候,还是需要用原生的View。

        A3: 如果使用Bee XML template而不使用xib, 是不能使用apple基于Constraint的自动布局的,原因是Bee自己实现了一套自动布局的机制。个人感觉这套布局机制基本能实现常规的布局需求,但要用好需要经验并了解原理。适配多种分辨率的时候,在XML template中似乎需要使用@media(device:){}语句块对特殊的尺寸布局,用百分比来布局会不够精确。关于BeeUILayout:
  •         被用来描述某个UI控件的布局信息,不是真正的UIView对象,只是UIView之间关系的描述,包含布局关系,风格等。
  •         Bee在创建UI控件之前,会解析XML,读入布局树和CSS风格信息,然后根据布局和风格信息创建UIView树结构。而布局树和风格信息存储在BeeUILayout对象里。
  •         Bee把对应XML的UIView对象用objc_setAssociatedObject和对应的BeeUILayout对象关联,每个BeeUILayout会持有子BeeUILayout对象集合(childs属性)。
  •         UI控件的RELAYOUT() block会重新排布子View, 根据是对应的BeeUILayout对象
        可以看到Bee对原生UIView树的扩展了BeeUILayout树。BeeUILayout会在第一次读入XML的时候创建。如果要运行时修改布局,可以:
  •         在运行时修改BeeUILayout对象。可以不用在修改UIView的frame,推荐。
  •         如果定制BeeUILayout对象难于实现需求,也可以修改所有相关UIView的frame,这样就回到了原生的做法,琐碎难以维护。
       现在来看一个具体的例子:首先用向导新建一个Bee View, 名字为TestCell, 基类选择BeeUICell, 选择自动生成XML模板。编辑TestCell.xml:
       
<ui namespace="TestCell">
    <linear id="layoutTest" orientation="v" class="wrapper">
        <button id="btn1" class="view gray">Hello</button>
        <label id="label1" class="view blue">World</label>
    </linear>
    
    <style type="text/css">
        .wrapper { width: 40px; height: auto;}
        .view { width: 40px; height: 20px;}
        .blue { background-color:#007DBC }
        .gray { background-color: #AEAEAE }
    </style>
</ui>
        这里使用"layoutTest"作为垂直的约束,包含一个灰色的Button, "Hello", 和一个蓝色的Label, "World"。看上去应该是这样:
        
        好了,我想插入两个这样的TestCell, 在一个BeeUIScrollView里显示。先封装两个TestCell, 新建一个名为BeCollectionCell的Bee View, xml的代码如下:
        
<ui namespace="BeCollectionCell">

    <TestCell id="test"/>
    <TestCell id="test1"/>
   
    <style type="text/css">
      #test {
      width: 100%;
      height: auto;
      }
      #test1 {
      width: 100%;
      height: auto;
      }

  </style>
</ui>
        包含两个TestCell, id分别为test, test1, 注意排布规则,在height上都是auto, 而在TestCell里,每个label/button高度是20px。注意XML的控件是可以嵌套的。找个BeeUIScrollView把这个BeCollectionCell插进去,代码可以参考BeeFramework的Dribble sample:
        
    self.list.whenReloading = ^
    {
        self.list.lineCount = 1;
        self.list.total     = 1;
            
        BeeUIScrollItem * item = self.list.items[0];
        item.clazz = [BeCollectionCell class];
        item.size = self.list.size;
        item.rule = BeeUIScrollLayoutRule_Tile;
        item.size = CGSizeAuto;
    }
        运行,可以看到效果如下:
        
        没有问题。现在有个如下的需求: 将第1个TestCell的label("World")删除掉,并自动排布。 这个例子的UIView树和BeeLayout的树结构如下:
        
        如题,黄色的矩形节点树代表UIView树,这里因为有两张XML template, 有两棵椭圆节点的BeeUILayout树,注意观察linear layout "layoutTest"的位置,现在要做的就是将左边那棵BeeUILayout树修改下,移除掉label1节点,然后调用BeCollectionCell的RELAYOUT。打开BeCollectionCell.m, 找到layoutDidFinish函数并添加编辑BeeUILayout子树的代码:
        
- (void)layoutDidFinish
{
    // TODO: custom layout here    
    if(_removed == NO)
    {
        //找到test的子UICell
        TestCell* subCell = (TestCell*)$(self).FIND(@"#test").view;
        
        //获得TestCell的全局layout
        BeeUILayout* cellLayout = [subCell layout];
        
        //获得子layout, 名为"layoutTest"
        NSMutableArray* children = [cellLayout childs];
        BeeUILayout* layout = [children objectAtIndex:0];
        
        //移除layoutTest的label, 位于第2个子layout
        NSMutableArray* subs = [layout childs];
        [subs removeObjectAtIndex:1];

        //成功后下次不再次删除
        _removed = YES;
        
        self.RELAYOUT();
    }
}
        运行程序,成功:
        
        以上就是动态调整Layout的办法,当然也可以直接操作UIView树,用removeFromSuperView方法,然后对剩余的UIView指定新的frame。

        A4: 可以参照Bee的XML模板说明(https://github.com/gavinkwoe/BeeFramework/wiki/Bee-Templates-Manual), 注意在布局约束上中Bee主要支持margin, padding, 像素数量,size百分比,停靠(align, float)等约束,理解了这些布局约束和CSS语法,编写BeeUI就没有问题。如果开发者了解Bee的UI是在原生的基础上扩展了Layout和Style, 并能自如得操作Layout和Style,相信使用起来更加顺手。大家注意还有一个很棒的调试功能:在模拟器中调试程序时,可以直接修改xml模板代码,界面排布会即时更新。

        最后,谢谢大家,祝大家春节快乐!Bee的分享暂时结束了,欢迎反馈,祝大家新年iOS更上台阶!
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值