zoukankan      html  css  js  c++  java
  • Sagit.Framework For IOS 开发框架入门教程3:Start引导页及框架布局和隐藏事件的内幕

    前言:

    框架依旧在快速更新着:在重构、简化代码,统一标准的过程中。

    中间也遇到各种坑,不过好在一步一脚印的解决了。

    虽然还有些功能还在思考,不过教程,还是得补上:

    上篇文章:Sagit.Framework For IOS 开发框架入门开发教程2:一行代码实现引导页

    里面讲到,引导完后,根据是否存在的Token来解决跳转到StartController还是MainController。

    这篇就写写StartController,实现的代码虽少,但原理很精彩!!!

    Sagit 实现登陆注册引导页

    从WelcomleController中,现在跳到了StartController了:

    呈现的内容如下图:(为不影响整体,这图宽高设的的很小,大伙可以新开窗口看大图):

    这个界面,除了基础的布局,还有两个事件:

    1:点立即注册:跳转到注册页。

    2:点登录:或跳转到登录页。

    整体的效果如下:

    这里把View和Controller分开文件处理:

    看看StartView的布局的全部代码:(下面是我本人简化后的代码,以前的代码多到吓死人)

    #import "STView.h"
    
    @interface StartView : STView  //StartView.h
    
    @end
    
    
    @implementation StartView    //StartView.m
    
    - (void)initUI
    {
      
    if(self.STController!=nil && self.STController.navigationController==nil){

          [self.STController asRoot:RootViewNavigationType];} 

        [self needNavigationBar:NO setNavBar:YES];//隐藏导航栏。
        
        [[[self addImageView:@"logo" imgName:@"login_logo"] 170 height:170] relate:LeftTopRight v:290 v2:200 v3:290];
        
        UILabel *title = [[[self addLabel:nil text:@"IT恋" font:48] 160 height:44] onBottom:@"logo" y:50];
        [[title textColor:@"#000000"] textAlignment:NSTextAlignmentCenter];
    
        UILabel *description = [[[[self addLabel:nil text:@"找优质靠谱IT男就上IT恋" font:36] 450 height:34] onBottom:title y:48] toCenter:X];
        [[description textColor:@"#000000"] textAlignment:NSTextAlignmentCenter];
        
    //框架调整,下面这几行代码有变化,具体见下文补充:
    UIButton
    *regBtn = [[[[self addButton:@"Reg" title:@"立即注册"] 287 height:77] onBottom:description y:484] toCenter:X]; [[regBtn backgroundImage:@"login_btn"] keyValue:@{@"leftNavImage":@"nav_arrow_left_black"}]; UIButton *loginBtn = [[[[self addButton:@"Login" title:@"已有账号,立即登录" font:24] 300 height:26] onBottom:regBtn y:60] toCenter:X]; [[loginBtn titleColor:MainHexColor] keyValue:STPreView.keyValue]; } @end

    新补充内容:

    新变化的两行代码的写法(新的调整是为了可以控制导航栏的整体,包括标题及右边的样式【原来只能控制左侧】):

    [[[[this addButton:@"Reg" title:@"立即注册"] 287 height:77] onBottom:STPreView y:484] toCenter:X];
        [[STLastButton backgroundImage:@"login_btn"] key:STNavConfig value:@{STNavTitle:@"账号注册",STNavLeftImage:@"nav_arrow_left_black"}];
        
        [[[[this addButton:@"Login" title:@"已有账号,立即登录" font:24] 300 height:26] onBottom:STPreView y:60] toCenter:X];
        [[STLastButton titleColor:MainHexColor] block:nil on:^(UIView *view) {
           [view key:STNavConfig value:@{STNavTitle:@"登录",STNavLeftImage:@"nav_arrow_left_black"}];
        }];

    代码功能讲解(第二点和第三点下面再细讲):

    1:基本上一个控件布局就一行代码,直接看过去就好了。
    
    2:第一行设置不需要导航栏,并直接隐藏导航栏:[self needNavigationBar:NO setNavBar:YES];//隐藏导航栏
    
    3:注册和登陆按钮,多了一个陌生的keyValue,因为这里要控制导航的返回按钮为自定义的图片。

    再看看StartController中的全部代码:

    @interface StartController : STController //StartController.h
    @end
    @implementation StartController //StartContrller.m -(instancetype)init { //初始化全局设置,必须要在UI初始之前。 [self configNavAndStatusBar]; return self; } -(void)configNavAndStatusBar {
        [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault animated:YES];//白色底,所以状态字颜色改为黑
        
        //这里提前统一设定全局协议的内容(对于登陆、注册、找回密码三个窗体有效),进入Main之后,会重新修改全局协议的内容
        [[[[[[UINavigationBar globalSetting] barTintColor:ColorWhite]tintColor:ColorBlack] backgroundImage:nil] shadowImage:nil]
         titleTextAttributes:@{NSForegroundColorAttributeName:ColorBlack}];
    }

    功能讲解:

    1:设置全局的导航栏和状态栏属性。 
    
    2:如果当前不是导航控制器,则设置自身为导航根控制器。

    Sagit 框架讲解:布局

    对于框架的布局:

    1:以【self addXXX】为起手势,一行代码实现一个UI的布局。
    
    2:对于需要特定类型的控制属性的,用变量接收后,用无限连语法处理属性赋值。
    
    3:相对父控件,用relate方法;相对同级,用:onTop、onLeft、onRight、onBottom方法,可以混着用,怎么简单怎么来。

    布局的方法,都抽到了以下STUIViewAutoLayout文件中:

    看看基本的方法重载:

    #pragma mark [相对布局方法] RelativeLayout
    -(UIView*)onRight:(id)uiOrName x:(CGFloat)x;
    -(UIView*)onRight:(id)uiOrName x:(CGFloat)x y:(CGFloat)y;
    -(UIView*)onLeft:(id)uiOrName x:(CGFloat)x;
    -(UIView*)onLeft:(id)uiOrName x:(CGFloat)x y:(CGFloat)y;
    -(UIView*)onTop:(id)uiOrName y:(CGFloat)y;
    -(UIView*)onTop:(id)uiOrName y:(CGFloat)y x:(CGFloat)x;
    -(UIView *)onBottom:(id)uiOrName y:(CGFloat)y;
    -(UIView *)onBottom:(id)uiOrName y:(CGFloat)y x:(CGFloat)x;
    -(UIView*)relate:(XYLocation)location v:(CGFloat)value;
    -(UIView*)relate:(XYLocation)location v:(CGFloat)value v2:(CGFloat)value2;
    -(UIView*)relate:(XYLocation)location v:(CGFloat)value v2:(CGFloat)value2 v3:(CGFloat)value3;
    -(UIView*)relate:(XYLocation)location v:(CGFloat)value v2:(CGFloat)value2 v3:(CGFloat)value3 v4:(CGFloat)value4;
    -(UIView*)toCenter;
    -(UIView*)toCenter:(XYFlag)xyFlag;

    这个很好理解的,基本读过去就明白了,简单易懂,其它的属性,等后续文章用到再说。

    Sagit 框架讲解:事件

    上一篇文章中,对事件有过一段讲解:

    框架对于UIView扩展了两种点击事件的绑定方式:

    #pragma mark 扩展系统事件
    -(UIView*)click:(NSString*)event;
    - (UIView*)addClick:(onClick)block;

    click用于指定一个事件的名称,addClick用于追加一个事件执行的代码块。

    也说了事件的寻址流程:

    1:先找传进来的event在所在的Controller中是否有对应的事件,若有,执行,若没有继续以下:
    
    2:对event追加后缀变成eventClick和eventClick:再看有没有对应的事件,若有,执行,若没有继续以下:
    
    3:对event追加后缀变成EventController,看有没有对应的控制器,若有,执行默认的open:事件跳转,若没有,则无绑定事件。

    不过上面的布局代码中并没使用click或addClick,同样是触发了这个流程:

    核心就在于UIButtton的name,如果一个按钮有name,则寻找事件,如果找到,就自动绑定事件。

    因此,对于两个name,Reg和Login:一路找到最终会找到RegController和LoginController,触发STController中预先定义的open:事件。

    Sagit 框架讲解:keyValue属性和[UINavigationBar globalSetting]

    1:keyValue属性

    IT恋这里有点特殊,跳转后需要改变导航栏的返回图标,原来在Controller中写事件:

    [self stPush:方法的第三个参数,指定一张图片做为返回按钮]

    不过对于有代码洁P的我,总想着怎么消灭掉这些这些代码,虽然两个事件就几行,但也不留。

    这个参数怎么传到open:里呢?如果不是图片,是指定文字为返回的按钮呢?

    最后想到一个相对完美的解决方案:

    1:对UIView再扩展了一个keyValue的属性,于是有了:

    keyValue已升级:新的写法见上面的补充代码。

    [[regBtn backgroundImage:@"login_btn"] keyValue:@{@"leftNavImage":@"nav_arrow_left_black"}];//这是旧代码,已过期不能用

    2:open:事件中,再进行一下的简单判断拿值(这里也已经调整过)。

    就这样完美的解决了。

    后来发现这个keyValue还有更多的用户场景,如:设置控制导航栏的显示或隐藏:

     [self needNavigationBar:NO setNavBar:YES];//隐藏导航栏。

    内部其实就是对keyValue进行取值或赋值:

    -(UIView*)needNavigationBar:(BOOL)yesNo setNavBar:(BOOL)setNavBar
    {
        if(self.keyValue==nil)
        {
            self.keyValue=[NSMutableDictionary new];
        }
        [self.keyValue set:@"needNavigationBar" value:yesNo?@"1":@"0"];
        if(setNavBar && self.STController!=nil && self.STController.navigationController!=nil)
        {
            self.STController.navigationController.navigationBar.hidden=!yesNo;
        }
        return self;
    }

    目前框架是自动控制导航栏的显示或隐藏,不需要用户去再操心的在每个页面都是写代码了。

    为了这个导航栏,真花我不少心力,特别是自定义返回和系统滑动返回,研究的过程都够另外再写一篇。

    2:UINavigationBar globalSetting 的产生:

     A:对于这个StartController这个页面,有以下几种情况会跳转过来:

    1:从欢迎引导页WelcomeController进来;
    
    2:用户进行系统后,点击退出时从SystemController进来;
    
    3:当前用户的Token数据失效,需要重新登陆时,从MainController中,跳进来;

    不管从哪个地方过来,由于自身需要占根视图,而为导航控制器,所以有一行代码:

    //从引导页跳转来时,需要将自身设置为导航根控制器
     if(self.STController.navigationController==nil){[self.STController asRoot:RootViewNavigationType];}

    框架对UIViewCtroller扩展了:asRoot方法,可以将当前Controller直接设置为根视图:

    //将当前视图设置为根视图
    -(UIViewController*)asRoot:(RootViewControllerType)rootViewControllerType{
        
        UIViewController *controller=self;
        if(rootViewControllerType==RootViewNavigationType)
        {
            controller = [[UINavigationController alloc]initWithRootViewController:self];
            //self.navigationController.navigationBar.hidden=!self.view.needNavigationBar;
        }
        [UIApplication sharedApplication].delegate.window.rootViewController=controller;
        return self;
    }

    B:导航栏进行统一的颜色风格处理(处理后,将对注册、登陆、找回密码等生效):

    之前的代码是这样的:

    框架封装完成属性无限连后可以这样:

    [[[[[[UINavigationBar globalSetting] barTintColor:ColorWhite]tintColor:ColorBlack] backgroundImage:nil] shadowImage:nil]
         titleTextAttributes:@{NSForegroundColorAttributeName:ColorBlack}];

    这里有几点坑和大伙分享:

    坑A:如何进行属性无限连

    [UINavigationBar appearance] 这里返回的是协议接口,并不是UINavigationBar实例

    一开始看到UINavigationBar去接收appearance的属性,聪明如我,就去扩展UINavigationBar的属性方法,然后打算用无限连简化。

    结果一运行就死,进入坑里徘徊了不少时间,最后才发现appearance返回的是UIAppearance接口,并不是UINavigationBar类型。

    但是UIAppearnce又不能直接用,也不能对协议接口做扩展,一时蒙了下B。

    然后绕到导航栏显示不显示、自定义返回和滑动返回,返回主界面没主动往上顶等坑里。

    坑里呆久出来后,想到另一种方式来实现无限连:

    通过一个静态方法返回一个自定义类,再由这个自定义类来连代码,像这样:

    @implementation UINavigationBar (ST)
    
    +(UINavigationBarSetting*)globalSetting
    {
        return [UINavigationBarSetting new];
    }
    @end
    
    @implementation UINavigationBarSetting
    #pragma mark 扩展系统属性
    -(UINavigationBarSetting*)tintColor:(id)colorOrHex
    {
        [UINavigationBar appearance].tintColor=[UIView toColor:(colorOrHex)];
        return self;
    }
    -(UINavigationBarSetting*)barTintColor:(id)colorOrHex
    {
        [UINavigationBar appearance].barTintColor=[UIView toColor:(colorOrHex)];
        return self;
    }

    坑B:为何全局设置无效

    以前的代码,先执行ViewDidLoad里的全局设置,再执行asRoot,触发导航栏,这样是正常的:

    -(void)viewWillAppear:(BOOL)animated
    {
        if(self.navigationController==nil)
        {
            [self asRoot:RootViewNavigationType];
            //self.navigationController.navigationBar.hidden = YES;
        }
    }

    但是这段代码被我消灭了,其它地方的跳转代码都是直接,退出后转跳:

    改完后,发现全局失效了,最后坑里呆了一圈才发现:

    全局的设置,必须在导航栏UI出现之前设置才有效,所以,如果这样写代码:

    全局设置就不能写在ViewDidLoad里了,必须写在init中了。

    总结:

    1:用框架写代码很简单,也很简洁。

    2:框架目前的代码不多,早看早了结。

    3:随着应用场景的增多,框架会不断的增强,预味着开发仍是很简单,但要理解原理就需要花更多时间。

    4:虽然教程是以IT恋为讲解,但还是希望大伙多关心IT连,哈。

  • 相关阅读:
    Dubbox小案例
    Maven项目
    网络命名空间和网桥的基本操作命令
    基于容器制作镜像
    docker命令的基本操作
    hbase 的一些坑
    并查集
    二叉树的递归遍历和非递归遍历
    比较器的使用
    用数组结构实现大小固定的队列和栈
  • 原文地址:https://www.cnblogs.com/cyq1162/p/8070502.html
Copyright © 2011-2022 走看看