前言:

框架还是在全速更新着:在重构、简化代码,统一标准的历程中。

中等也蒙受种种坑,但是幸好一步1足迹的消除了。

固然如此还有些作用还在思考,但是教程,照旧得补上:

上篇作品:Sagit.Framework For IOS
开采框架入门开采教程二:一行代码实现根据各州的具体情况制定方案页

内部讲到,引导完后,依据是或不是留存的Token来消除跳转到StartController照旧MainController。

那篇就写写StartController,完结的代码虽少,但原理相当漂亮貌!!!

前言:

话说今早如故明儿早上,写了一篇:讲述Sagit.Framework消除:双向引用导致的IOS内存泄漏(上)

小说写到最后时,多了广大莫名神奇的难点!!!

为了缓慢解决了那么些莫名奇妙的难点,笔者又战争了24钟头〜〜〜

然后终于化解了难题,原本是IOS的隐藏性Bug,只想恨恨的说一声fuck~~~

Sagit 完结登入注册指导页

从WelcomleController中,以往跳到了StartController了:

展现的内容如下图:(为不影响全部,那图宽高设的的非常小,大伙能够新开窗口看大图):

会员登陆 1

那些分界面,除了基础的布局,还应该有八个事件:

一:点立马登记:跳转到注册页。

贰:点登入:或跳转到登六页。

完全的功力如下:

会员登陆 2

此间把View和Controller分开文件管理:

会员登陆 3

看望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"] width:170 height:170] relate:LeftTopRight v:290 v2:200 v3:290];

    UILabel *title = [[[self addLabel:nil text:@"IT恋" font:48] width:160 height:44] onBottom:@"logo" y:50];
    [[title textColor:@"#000000"] textAlignment:NSTextAlignmentCenter];

    UILabel *description = [[[[self addLabel:nil text:@"找优质靠谱IT男就上IT恋" font:36] width:450 height:34] onBottom:title y:48] toCenter:X];
    [[description textColor:@"#000000"] textAlignment:NSTextAlignmentCenter];

    //框架调整,下面这几行代码有变化,具体见下文补充:

    UIButton *regBtn = [[[[self addButton:@"Reg" title:@"立即注册"] width: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] width:300 height:26] onBottom:regBtn y:60] toCenter:X];
    [[loginBtn titleColor:MainHexColor] keyValue:STPreView.keyValue];
}


@end

新添补内容:

新变化的两行代码的写法(新的调解是为了能够操纵导航栏的完整,包罗标题及左侧的体裁【原本只好调控左侧】):

[[[[this addButton:@"Reg" title:@"立即注册"] width: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] width: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:如果当前不是导航控制器,则设置自身为导航根控制器。

轶事出自:

旧事是那般的,为了管理内部存款和储蓄器释放的难题,寻常人的合计,都以给指标的dealloc扩张日志输出。

于是,UIView、UIViewController和多个Sagit定义的基类S电视机iew、STController,任天由命就增进了如此1行代码:

-(void)dealloc{
    NSLog(@"UIView relase -> %@", [self class]);
}

然后就经过输出的日志,观望该措施被实践与否,来认同对象是或不是健康被假释。

在各个强引用、弱引用、循环引用、先强后弱引用的坑里跳来跳去后,终于祭出了圆满的杀招,来拍卖这么些标题:

-(id)key:(NSString *)key
{
    id value=[self.keyValue get:key];
    if(value==nil)
    {
        value=[self.keyValueWeak get:key];
    }
    return value;
}
-(UIView*)key:(NSString *)key valueWeak:(id)value
{
    [self.keyValueWeak set:key value:value];
    return self;
}
-(UIView*)key:(NSString *)key value:(id)value
{
    [self.keyValue set:key value:value];
    return self;
}
-(NSMapTable*)keyValueWeak
{
    NSMapTable *kv=[self.keyValue get:@"keyValueWeak"];
    if(kv==nil)
    {
        kv=[NSMapTable mapTableWithKeyOptions:NSMapTableWeakMemory valueOptions:NSMapTableWeakMemory];
        [self.keyValue set:@"keyValueWeak" value:kv];
    }
    return kv;
//    NSMapTable *kv= (NSMapTable*)objc_getAssociatedObject(self, &keyValueWeakChar);
//    if(kv==nil)
//    {
//        kv=[NSMapTable mapTableWithKeyOptions:NSMapTableWeakMemory valueOptions:NSMapTableWeakMemory];
//        objc_setAssociatedObject(self, &keyValueWeakChar, kv,OBJC_ASSOCIATION_RETAIN);
//    }
//    return kv;
}

-(NSMutableDictionary<NSString*,id>*)keyValue
{

    NSMutableDictionary<NSString*,id> *kv= (NSMutableDictionary<NSString*,id>*)objc_getAssociatedObject(self, &keyValueChar);
    if(kv==nil)
    {
        kv=[NSMutableDictionary<NSString*,id> new];
        objc_setAssociatedObject(self, &keyValueChar, kv,OBJC_ASSOCIATION_RETAIN);
    }
    return kv;
}

通过在强引用的Dictionary里,保存二个弱引用NSTableMap,来存档一些内需弱引用的靶子,比方View或Controller等对象。

正当自家在揣摩上边包车型客车解法的时候:另贰个十分的导航栏Crash难题也油不过生了,而且在以下三种状态都会Crash掉:

Sagit 框架讲授:布局

对于框架的布局:

1:以【self addXXX】为起手势,一行代码实现一个UI的布局。

2:对于需要特定类型的控制属性的,用变量接收后,用无限连语法处理属性赋值。

3:相对父控件,用relate方法;相对同级,用:onTop、onLeft、onRight、onBottom方法,可以混着用,怎么简单怎么来。

布局的章程,都抽到了以下STUIViewAutoLayout文件中:

会员登陆 4

看望基本的不二秘诀重载:

#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;

其1很好领悟的,基本读过去就清楚了,简单易懂,别的的本性,等后续小说用到再说。

导航栏命案一:回退即Crash

 1方始是通过导航栏回退看日志输出,结果却动不动给作者来那几个:

会员登陆 5

重在还怎么全局断点、僵使对象等艺术都行不通,只好靠猜〜〜〜〜〜

拍卖这些标题吗,幸而,小编是把代码折半注释,最后牢固到:

会员登陆 6

圈起来的代码,是为每2个UI,都加多三个属性,前1个UI和后1个UI。

化解也很轻便,用弱引用存档就好了(那年,强弱引用的难题早就被自个儿完美化解了):

// Name
- (UIView*)preView{
    return [self key:@"preView"];
}
- (UIView*)preView:(UIView*)view
{
    return [self key:@"preView" valueWeak:view];
}

- (UIView*)nextView{
    return [self key:@"nextView"];
}
- (UIView*)nextView:(UIView*)view
{
    return [self key:@"nextView" valueWeak:view];
}

Sagit 框架解说:事件

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

框架对于UIView扩充了二种点击事件的绑定格局:

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

click用于钦定三个事变的名目,addClick用于追加2个风云施行的代码块。

也说了轩然大波的寻址流程:

1:先找传进来的event在所在的Controller中是否有对应的事件,若有,执行,若没有继续以下:

2:对event追加后缀变成eventClick和eventClick:再看有没有对应的事件,若有,执行,若没有继续以下:

3:对event追加后缀变成EventController,看有没有对应的控制器,若有,执行默认的open:事件跳转,若没有,则无绑定事件。

而是下面的布局代码中并没利用click或addClick,同样是触发了那一个流程:

骨干就在于UIButtton的name,假如二个按键有name,则寻找事件,假若找到,就活动绑定事件。

为此,对于五个name,Reg和Login:一路找到最终会找到RegController和LoginController,触发STController中开始时期定义的open:事件。

导航栏命案二:回退几遍即Crash

上一回是援引难点,这三次啊?我X,错误的提示,照旧和上海图书馆1律,3个main含数里让您猜〜〜〜〜〜

会员登陆 7

再者拔尖就平时,二级就挂给你看〜〜〜真不要脸。

接下来,正是针对这种灵异事件,各个渡,发掘全球都没出现自个儿那些主题素材〜〜〜〜那真神了去了。

下一场又起来注释代码,美妙的开掘,在弹回后退时,如若把上一个地方栏给重置三次,即不会油然则生Crash现象。

故而,笔者是这么思量导航栏难题的:

    1: 一个navigationCtroller只有一个navigationBar。
    2: 每一个viewController,都会复写前一个navigationBar。
    3: 所以,到下一层时,UINavigationBar指向最后一个Controller
    4: 当回退时,最后一个Controller被释放后,navigationBar没有被重绘的话,事件指向就会出问题。

下一场又开端最先保存当前状态,然后后退时还原状态那条路。

由于后退是自定义事件,所以可以在事变里加代码还原,可是假若是滑动重回吗?

千身万苦之后,找到:shouldPopItem,在此间能够做点事情:

@implementation UINavigationController (ST)

#pragma mark NavigationBar 的协议,这里触发
// fuck shouldPopItem 方法存在时,只会触发导航栏后退,界面视图却不后退。
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item  // same as push methods
{
    //重设上一个Controller的导航(不然在二次Push后再Pop会Crash)
    NSInteger count=self.viewControllers.count;
    if(count>0)//发现这里返回的viewControllers,已经是移掉了当前的Controller后剩下的。
    {
        UIViewController *preController=self.viewControllers[count-1];//获取上一个控制器
        if([preController needNavBar])
        {
            [preController reSetNav:self];
        }
    }

    return YES;
}
//返回到当前页面
- (void)navigationBar:(UINavigationBar *)navigationBar didPopItem:(UINavigationItem *)item
{
//    if(navigationBar!=nil && [navigationBar.lastSubView isKindOfClass:[UIButton class]])
//    {
//       // [navigationBar.lastSubView height:0];//取消自定义复盖的UIButton
//    }
    NSInteger count=self.viewControllers.count;
    if(count>0)
    {
        UIViewController *current=self.viewControllers[count-1];
        self.navigationBar.hidden=![current needNavBar];
//        if(!self.navigationBar.isHidden)
//        {
//            [current reSetNav:self];
//        }
        if(self.tabBarController!=nil)
        {
            self.tabBarController.tabBar.hidden=![current needTabBar];
        }
    }
}
-(void)dealloc
{
    NSLog(@"UINavigationController relase -> %@", [self class]);
}
@end

改完现在,开掘全体又健康了,然后,又迎来了导航栏的第三波bug。

(PS:shouldPopItem
那一个方法,是第1个一级Bug坑)

Sagit 框架批注:keyValue属性和[UINavigationBar globalSetting]

导航栏命案三:第1个页面存在自定义开关,则Crash

以下这么些分界面,右上角二个小相机事件。

会员登陆 8

因为化解难点二的代码中,并不曾回复第一个页的导航栏,所以事故依旧时有爆发了。

接下来又沉思,那暗许的第三个页面状态怎么保存?

直白保存整个UIButtonBar也许全部NavigationBar,再恢复生机,竟然分外!!!

几乎欲哭无泪,种种渡,仍无果,为什么全世界,都没这种主题素材吗?

莫不是整个世界写代码都以内存不自由,所以木有那几个问题?

诸如此类基础的标题,不是所在都会遭逢的么?

然后到了隔壁小伙的管理器上,把这种美妙的好玩的事,在他计算机上再度演示了二遍!!!

果真仍然和自己Computer上的平等!!!

果然那标题或许很广泛,为啥呢,渡不出果呢?

不掌握为何,作为一名菜鸟,他跑去把这段代码给注释了:

//fuck dealloc 方法存在时,会影响导航的后退事件Crash,以下两种情况:1:当前UI有自定义导航按钮时;2:Push两层再回退。
//-(void)dealloc
//{
////    if(self.gestureRecognizers.count>0)
////    {
////        if(self.gestureRecognizers!=nil)
////        {
////            for (NSInteger i=self.gestureRecognizers.count-1; i>=0; i--)
////            {
////                [self removeGestureRecognizer:self.gestureRecognizers[i]];
////            }
////        }
////    }
//    //[self.keyValueWeak removeAllObjects];
//  //  [self.keyValue removeAllObjects];
//
//    NSLog(@"UIView relase -> %@ name:%@", [self class],self.name);
//}

然后,一切寻常了〜〜〜〜咦!!!!

是dealloc里的代码不寻常引发的?

接下来里面代码全注释掉了,难点要么有!!!

把dealloc整个注释掉,又健康了!!!

1:keyValue属性

IT恋这里有一点点新鲜,跳转后需求转移导航栏的归来Logo,原本在Controller中写事件:

[self stPush:方法的第多少个参数,钦命一张图纸做为重返开关]

会员登陆 9

可是对于有代码洁P的本身,总想着怎么消灭掉这一个那一个代码,就算七个事件就几行,但也不留。

其一参数怎么传到open:里吧?若是还是不是图片,是点名文字为回到的开关呢?

终极想到一个周旋圆满的缓和方案:

一:对UIView再扩张了3个keyValue的性质,于是有了:

keyValue已升格:新的写法见下面的补充代码。

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

二:open:事件中,再拓展一下的简约判别拿值(此间也壹度调解过)。

会员登陆 10

就这么健全的解决了。

新生意识这一个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;
}

现阶段框架是电动调控导航栏的显示或隐匿,不必要用户去再担忧的在各类页面都以写代码了。

为了这一个导航栏,真花自身十分多头脑,非常是自定义重返和类别滑动重返,切磋的长河都够此外再写1篇。

本质:IOS的Bug,无法扩张UIView的dealloc方法

到了那边,真相到底出来了,只要扩展了UIView的dealloc方法,导航栏就敢死给你看!!!

小编了个去,为了查内部存款和储蓄器释放,所以要写dealloc方法,但写了这几个方式,就引出来这么多奇妙的灵异事件。 

无语啊,那是为了让我们毫不管内部存款和储蓄器释放难点,故意设下的坑么。

IOS除了那么些Bug,还应该有极其shouldPopItem事件,只要那几个事件存在,暗中同意return
YES,就只重返导航头,不会回来分界面,也是个坑!!!

功能是那样的:

会员登陆 11

2:UINavigationBar globalSetting 的产生:

总结:

就是人算比不上天算,境遇这种坑,也是全宇宙第1位了。

那边给大家提供2个坑队友的的新章程:

找个地点,对UIView扩充两个dealloc空方法。

下一场就说那Bug相当美丽妙,让他扶助看看,包对方头大两尺三〜〜〜

当然内部存款和储蓄器泄漏的难点,到此篇就停止的,不过还恐怕有二个Infiniti制的主题材料想消除:

愿意在block里,猖獗的写self,也不会促成内部存款和储蓄器汇漏难点。

又经过4捌钟头的血战,终于消除了。

并且又开采另一个IOS的坑,好呢,只能把此文字改正成人中学,计划再来一篇下。

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

1:从欢迎引导页WelcomeController进来;

2:用户进行系统后,点击退出时从SystemController进来;

3:当前用户的Token数据失效,需要重新登陆时,从MainController中,跳进来;

不论是从哪些地点过来,由于自家须要占根视图,而为导航调控器,所以有1行代码:

//从引导页跳转来时,需要将自身设置为导航根控制器
 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:导航栏进行合并的颜色风格管理(管理后,将对登记、登录、找回密码等生效):

以前的代码是如此的:

会员登陆 12

框架封装实现质量Infiniti连后得以这么:

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

此处有几点坑和大伙儿分享:

坑A:怎样实行品质Infiniti连

[UINavigationBar appearance]
这里重返的是说道接口,并不是UINavigationBar实例

一早先看到UINavigationBar去接收appearance的质量,聪明如我,就去扩展UINavigationBar的性质方法,然后计划用Infiniti连简化。

结果一运维就死,进入坑里徘徊了重重光阴,最后才意识appearance再次来到的是UIAppearance接口,并不是UINavigationBar类型。

可是UIAppearnce又不可能一贯用,也无法对协商接口做扩充,一时蒙了下B。

接下来绕到导航栏呈现不显得、自定义重回和滑动再次来到,再次回到主分界面没主动往上顶等坑里。

坑里呆久出来后,想到另一种方法来促成Infiniti连:

因此1个静态方法重临2个自定义类,再由这一个自定义类来连代码,像这么:

@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;
    }
}

可是这段代码被笔者消灭了,别的地点的跳转代码都以直接,退出后转跳:

会员登陆 13

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

全局的装置,必须在导航栏UI出现在此以前设置才使得,所以,要是那样写代码:

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

总结:

一:用框架写代码很简短,也很轻易。

2:框架近来的代码相当少,早看早了结。

3:随着应用场景的增添,框架会频频的增高,预味着开垦仍是很轻便,但要精晓原理就供给花越多日子。

四:即便教程是以IT恋为教学,但依旧希望我们多关注IT连,哈。

相关文章