利用UITabBarController搭建项目的主界面---各种封装

一:什么时候需要进行封装

1:写代码处处要有封装思想:封装好的代码要基本符合以下三个条件:1:封装的部分向外界提供的接口调用最简洁方便  2:封装的时候要考虑项目的拓展性 3:封装以后要想代码量更加简洁

2:什么时候需要封装:具有相同业务逻辑的部分、或是调用频率很高的大量重复代码、或是将UI部分某些通用的模块等都应该采取封装、封装的MVC模型:mvc三个各司其职,各自负责自身内部的业务逻辑,把自身内部的业务逻辑搜封装在相应模块的内部

3:如何封装:把业务逻辑部分与外界无关的都封装在公共代码的内部,封装的部分再向外界提供接口访问,封装之后要满足封装的三个条件

二:利用UITabBarController搭建项目的主界面---各种封装

1:自定义的tabBar的封装

1:在外部的调用:初始化init方法,设置代理用于点击tabbar按钮对点击事件的回调,items是向外界提供的接口,自定义tabBar,加在self.view上后需要移除系统的tabBar

2:tabBar内部封装的代码

#import <UIKit/UIKit.h>

/*
1:回调采用协议代理:也可采用通知,block
2:协议代理:
 1:在定义协议时,可以通过@required与@optional来配置遵守这个协议必须去实现的方法和可以选择的方法。譬如:

 @protocol MyChooseDayViewDelegate<NSObject>
 @required
 - returnChooseDay:(id)myChooseDayView;
 @optional
 - hideThisView;
 - changeDateFormatter:(NSString*)formatString;
 @end
 在定义的协议 MyChooseDayViewDelegate中,如果遵守该协议,就必须实现协议中的- returnChooseDay: 方法,同时可以根据实际程序要求去实现- hideThisView 与 - changeDateFormatter: 。

 同时,协议支持对本身的一种扩展,譬如:

 @protocol MyChooseTimeViewDelegate< MyChooseDayViewDelegate>
 - doSomething;
 @end
 MyChooseTimeViewDelegate扩展了MyChooseDayViewDelegate这个协议,也就是说,假如遵守了MyChooseTimeViewDelegate的话,也必须实现MyChooseDayViewDelegate中的方法(@required)使用分类的话,就是在定义类的头文件中使用<> 将所需要的协议引入,如果引入多个协议,用","分隔,譬如:

 @interface MyClassView:UIViewController <MyChooseDayViewDelegate, UIAlertViewDelegate>
 //TODO: balabalabala...
 @end

 想要检查某个类是否实现了某个协议或者某个类是否实现了某个协议的方法,可以通过以下方式来进行测试

 //获取某个对象
 MyClassView *myClassView = xxxxxxx;
 //判断该对象是否实现了MyChooseDayViewDelegate协议
 if([myClassView conformToProtocol:@protocol (MyChooseDayViewDelegate)]){
 //TODO: balabalabala...
 }
 //判断对象是否实现了某个方法就用我们经常会使用到的
 if([myClassView responseToSelector:@selector(xxxx)]){
 //TODO: balabalabala...
 }

 2:协议代理以属性定义代理时用weak修饰:
 @property (nonatomic, weak) id<CZTabBarDelegate> delegate;

 */
@class CZTabBar;
@protocol CZTabBarDelegate <NSObject>

@optional
- (void)tabBar:(CZTabBar *)tabBar didClickButton:(NSInteger)index;//点击tabBar按钮的切换子控制器的回调
- (void)tabBarDidClickPlusButton:(CZTabBar *)tabBar;//点击中间加号按钮的回调

@end

@interface CZTabBar : UIView
// items:保存每一个按钮对应tabBarItem模型
@property (nonatomic, strong) NSArray *items;

@property (nonatomic, weak) id<CZTabBarDelegate> delegate;

@end

补充说明:1:协议代理中代理属性为什么要用Weak修饰?

原因:为了避免循环引用,防止内存泄露。在LL中以属性定义baby类,或是创建对象加在ll上,则ll对baby类有一个强引用,并指向baby类,

如果weak的话,在程序运行的时候不会造成循环引用,对象都会被顺利的销毁,所以会调用婴儿类和保姆类的delloc方法如果strong的话,在程序运行的时候会造成循环引用(意思就是reatainCount不为0,只要有实线引用,计数器就+1),对象都不会的销毁,所以会调用婴儿类和保姆类不会调用delloc方法,从而造成了内存泄露的问题

#import "CZTabBar.h"

#import "CZTabBarButton.h"

@interface CZTabBar ()

@property (nonatomic, weak) UIButton *plusButton;//中间的加号按钮

@property (nonatomic, strong) NSMutableArray *buttons;

@property (nonatomic, weak) UIButton *selectedButton;//定义按钮切换的中间变量

@end

@implementation CZTabBar

#pragma mark -- 懒加载数组
- (NSMutableArray *)buttons
{
    if (_buttons == nil) {
        _buttons = [NSMutableArray array];
    }
    return _buttons;
}

#pragma mark -- 重写存在TabBarItem数组的set方法
- (void)setItems:(NSArray *)items
{
    _items = items;

    // 1:遍历模型数组,创建对应tabBarButton
    for (UITabBarItem *item in _items) {

        CZTabBarButton *btn = [CZTabBarButton buttonWithType:UIButtonTypeCustom];

    // 2:给按钮赋值模型,按钮的内容由模型对应决定
        btn.item = item;
        btn.tag = self.buttons.count;
        [btn addTarget:self action:@selector(btnClick:) forControlEvents:UIControlEventTouchDown];

        if (btn.tag == 0) { // 默认选中第0个
            [self btnClick:btn];

        }

        [self addSubview:btn];
        // 把按钮添加到按钮数组
        [self.buttons addObject:btn];
    }
}

#pragma mark -- 点击tabBarButton调用
-(void)btnClick:(UIButton *)button
{
    //1:改变按钮的状态
    _selectedButton.selected = NO;
    button.selected = YES;
    _selectedButton = button;

    //2:通知tabBarVc切换控制器,
    if ([_delegate respondsToSelector:@selector(tabBar:didClickButton:)]) {
        [_delegate tabBar:self didClickButton:button.tag];
    }
}

#pragma mark -- 点击懒加载加号按钮,在懒加载时可以设置按钮的一些属性
- (UIButton *)plusButton
{
    if (_plusButton == nil) {

        //1:设置按钮的图片与背景图片
        UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
        [btn setImage:[UIImage imageNamed:@"tabbar_compose_icon_add"] forState:UIControlStateNormal];
        [btn setImage:[UIImage imageNamed:@"tabbar_compose_background_icon_add"] forState:UIControlStateHighlighted];
        [btn setBackgroundImage:[UIImage imageNamed:@"tabbar_compose_button"] forState:UIControlStateNormal];
        [btn setBackgroundImage:[UIImage imageNamed:@"tabbar_compose_button_highlighted"] forState:UIControlStateHighlighted];

        // 2:默认按钮的尺寸跟背景图片一样大,sizeToFit:默认会根据按钮的背景图片或者image和文字计算出按钮的最合适的尺寸,sizeTofit:可以让lable自适应宽高:适应高度的时候必须设置 lable.numberoflines = 0;用法就是:正常创建lable设置frame,设置lable.numberoflines = 0,并设置[lable sizeTofit]
        [btn sizeToFit];

        // 监听按钮的点击
        [btn addTarget:self action:@selector(plusClick) forControlEvents:UIControlEventTouchUpInside];

        _plusButton = btn;

        [self addSubview:_plusButton];

    }
    return _plusButton;
}

#pragma mark -- 点击加号按钮的时候调用
- (void)plusClick
{
    // modal出控制器
    if ([_delegate respondsToSelector:@selector(tabBarDidClickPlusButton:)]) {
        [_delegate tabBarDidClickPlusButton:self];
    }
}

// self.items UITabBarItem模型,有多少个子控制器就有多少个UITabBarItem模型
#pragma mark 调整子控件的位置
- (void)layoutSubviews
{
    /*
     1:
     layoutSubviews在以下情况下会被调用:
     1、init初始化不会触发layoutSubviews,但是是用initWithFrame 进行初始化时,当rect的值 非CGRectZero时,也会触发。
     2、addSubview会触发layoutSubviews
     3、设置view的Frame会触发layoutSubviews,当然前提是frame的值设置前后发生了变化
     4、滚动一个UIScrollView会触发layoutSubviews
     5、旋转Screen会触发父UIView上的layoutSubviews事件
     6、改变一个UIView大小的时候也会触发父UIView上的layoutSubviews事件

     2:刷新子对象布局??

     ####1.什么时候,需要重写??###

     + view是系统的,不需要重写 - (void)layoutSubviews

     + view是自定义的,需要重写  - (void)layoutSubviews

     + -layoutSubviews方法:这个方法,默认没有做任何事情,需要子类进行重写,自定义view时,手动重写,这里面只能写subview的frame限制。

     ####2.手动调用这个方法,系统默认 我们不能手动直接调用这个方法,这能通过下列两种方式,调用/触发 - (void)layoutSubviews方法####

     + -setNeedsLayout方法: 标记为需要重新布局,告诉系统未来某个时间点异步调用。不立即刷新,但layoutSubviews一定会被调用。

     + -layoutIfNeeded方法:如果,有需要刷新的标记,立即调用layoutSubviews进行布局(如果没有标记,不会调用layoutSubviews)

     + 若需要立即刷新view的frame更改:(同时调用,注意先后顺序)

     *先调用[view setNeedsLayout],把标记设为需要布局

     *然后马上调用[view layoutIfNeeded],实现布局

     + 在初始化方法init..。、或者view第一次显示之前,标记总是“需要刷新”的,可以直接调用[view layoutIfNeeded]

     文/zhb_jianshu(简书作者)
     原文链接:http://www.jianshu.com/p/ed28cbeae943
     著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。
     */
    [super layoutSubviews];

    //1:设置子控件的frame
    CGFloat w = self.bounds.size.width;
    CGFloat h = self.bounds.size.height;
    CGFloat btnX = 0;
    CGFloat btnY = 0;
    CGFloat btnW = w / (self.items.count + 1);
    CGFloat btnH = self.bounds.size.height;

    int i = 0;
    // 设置tabBarButton的frame,UIView *tabBarButton,多态,父类的指针可以指向子类
    for (UIView *tabBarButton in self.buttons) {
        if (i == 2) {
            i = 3;
        }
        btnX = i * btnW;
        tabBarButton.frame = CGRectMake(btnX, btnY, btnW, btnH);
        i++;
    }

    // 设置添加按钮的位置
    self.plusButton.center = CGPointMake(w * 0.5, h * 0.5);

}

@end

3:CZTabBarButton内部封装的代码

#import <UIKit/UIKit.h>

@interface CZTabBarButton : UIButton

@property (nonatomic, strong) UITabBarItem *item;

@end
#import "CZTabBarButton.h"
#import "CZBadgeView.h"
#define CZImageRidio 0.7

@interface CZTabBarButton ()
/*
 用weak和strong都不会报错,用strong引用计数会加1,加到self上时,引用计数也会加1,为了保证引用计数为0,用weak修饰
 */
@property (nonatomic, weak) CZBadgeView *badgeView;

@end

@implementation CZTabBarButton

#pragma mark --重写setHighlighted,取消高亮做的事情
/*
 当不需要按钮的高亮行为时,可以重写按钮的高亮方法,不调用父类的方法,取消按钮的高粱状态
 */
- (void)setHighlighted:(BOOL)highlighted{}

#pragma mark -- 懒加载badgeView
- (CZBadgeView *)badgeView
{
    if (_badgeView == nil) {
        CZBadgeView *btn = [CZBadgeView buttonWithType:UIButtonTypeCustom];

        [self addSubview:btn];

        _badgeView = btn;
    }

    return _badgeView;
}
#pragma mark --按钮的初始化方法
/*
 1:一般在初始化方法中会设置控件的一些属性,基本都写在初始化方法中
 2:imageView的几种视图模式:UIViewContentModeScaleAspectFit:这个图片都会在view里面显示,并且比例不变 这就是说 如果图片和view的比例不一样 就会有留白 UIViewContentModeScaleAspectFill, // 这是整个view会被图片填满,图片比例不变 ,这样图片显示就会大于view。可以用一下代码处理[self.prp_imageViewsetContentMode:UIViewContentModeScaleAspectFill];
     self.prp_imageView.clipsToBounds = YES;//表示裁剪去多余的部分
     UIViewContentModeCenter:图片居中
     UIViewContentModeScaleToFill: 根据视图的比例去拉伸图片内容。
 **/
- (instancetype)initWithFrame:(CGRect)frame
{
    if (self = [super initWithFrame:frame]) {
        // 1:设置字体颜色
        [self setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
        [self setTitleColor:[UIColor orangeColor] forState:UIControlStateSelected];

        // 2:图片居中
        self.imageView.contentMode = UIViewContentModeCenter;
        // 3:文字居中
        self.titleLabel.textAlignment = NSTextAlignmentCenter;
        // 4:设置文字字体:加粗字体用+ (UIFont *)boldSystemFontOfSize:(CGFloat)fontSize方法
        self.titleLabel.font = [UIFont systemFontOfSize:12];

    }
    return self;
}
#pragma mark -- 传递UITabBarItem给tabBarButton,给tabBarButton内容赋值
/*
 1:说明只有通过键值编码(KVC)改变的值,才会回调观察者注册的方法。
 2:首先,假设我们的目标是在一个UITableViewController内对tableview的contentOffset进行实时监测,很容易地使用KVO来实现为。

 在初始化方法中加入:
 1

 [_tableView addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew context:nil];

 在dealloc中移除KVO监听:
 1

 [_tableView removeObserver:self forKeyPath:@"contentOffset" context:nil];

 添加默认的响应回调方法:

 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
 change:(NSDictionary *)change context:(void *)context
 {
 [self doSomethingWhenContentOffsetChanges];
 }

 好了,KVO实现就到此完美结束了,拜拜。。。开个玩笑,肯定没这么简单的,这样的代码太粗糙了,当你在controller中添加多个KVO时,所有的回调都是走同上述函数,那就必须对触发回调函数的来源进行判断。判断如下:

 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
 change:(NSDictionary *)change context:(void *)context
 {
 if (object == _tableView && [keyPath isEqualToString:@"contentOffset"]) {
 1

 [self doSomethingWhenContentOffsetChanges];
 1

 } }

 你以为这样就结束了吗?答案是否定的!我们假设当前类(在例子中为UITableViewController)还有父类,并且父类也有自己绑定了一些其他KVO呢?我们看到,上述回调函数体中只有一个判断,如果这个if不成立,这次KVO事件的触发就会到此中断了。但事实上,若当前类无法捕捉到这个KVO,那很有可能是在他的superClass,或者super-superClass...中,上述处理砍断了这个链。合理的处理方式应该是这样的:

 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
 change:(NSDictionary *)change context:(void *)context
 {
 if (object == _tableView && [keyPath isEqualToString:@"contentOffset"]) {
 [self doSomethingWhenContentOffsetChanges];
 } else {
 [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
 }
 }

 这样就结束了吗?答案仍旧是否定的。潜在的问题有可能出现在dealloc中对KVO的注销上。KVO的一种缺陷(其实不能称为缺陷,应该称为特性)是,当对同一个keypath进行两次removeObserver时会导致程序crash,这种情况常常出现在父类有一个kvo,父类在dealloc中remove了一次,子类又remove了一次的情况下。不要以为这种情况很少出现!当你封装framework开源给别人用或者多人协作开发时是有可能出现的,而且这种crash很难发现。不知道你发现没,目前的代码中context字段都是nil,那能否利用该字段来标识出到底kvo是superClass注册的,还是self注册的?

 回答是可以的。我们可以分别在父类以及本类中定义各自的context字符串,比如在本类中定义context为@"ThisIsMyKVOContextNotSuper";然后在dealloc中remove observer时指定移除的自身添加的observer。这样iOS就能知道移除的是自己的kvo,而不是父类中的kvo,避免二次remove造成crash。

 */

- (void)setItem:(UITabBarItem *)item
{
    _item = item;

    [self observeValueForKeyPath:nil ofObject:nil change:nil context:nil];

    // KVO:时刻监听一个对象的属性有没有改变
    // 给谁添加观察者
    // Observer:按钮
    [item addObserver:self forKeyPath:@"title" options:NSKeyValueObservingOptionNew context:nil];
    [item addObserver:self forKeyPath:@"image" options:NSKeyValueObservingOptionNew context:nil];
    [item addObserver:self forKeyPath:@"selectedImage" options:NSKeyValueObservingOptionNew context:nil];
    [item addObserver:self forKeyPath:@"badgeValue" options:NSKeyValueObservingOptionNew context:nil];

}

#pragma mark -- 只要监听的属性一有新值,就会调用
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{

    [self setTitle:_item.title forState:UIControlStateNormal];

    [self setImage:_item.image forState:UIControlStateNormal];

    [self setImage:_item.selectedImage forState:UIControlStateSelected];

    // 设置badgeValue
    self.badgeView.badgeValue = _item.badgeValue;
}

#pragma mark -- 修改按钮内部子控件的frame
- (void)layoutSubviews
{
    [super layoutSubviews];

    // 1.imageView
    CGFloat imageX = 0;
    CGFloat imageY = 0;
    CGFloat imageW = self.bounds.size.width;
    CGFloat imageH = self.bounds.size.height * CZImageRidio;
    self.imageView.frame = CGRectMake(imageX, imageY, imageW, imageH);

    // 2.title
    CGFloat titleX = 0;
    CGFloat titleY = imageH - 3;
    CGFloat titleW = self.bounds.size.width;
    CGFloat titleH = self.bounds.size.height - titleY;
    self.titleLabel.frame = CGRectMake(titleX, titleY, titleW, titleH);

    // 3.badgeView
    self.badgeView.x = self.width - self.badgeView.width - 10;
    self.badgeView.y = 0;
}

@end


4:CZBadgeView 内部封装的代码

#import <UIKit/UIKit.h>

@interface CZBadgeView : UIButton

@property (nonatomic, copy) NSString *badgeValue;

@end
#import "CZBadgeView.h"

#define CZBadgeViewFont [UIFont systemFontOfSize:11]

@implementation CZBadgeView

/*
 1:外部只设置了CZBadgeView的xy,size没有设置,因为未读数是一直变化的,所以大小也会改变,所以内部用sizeToFit,会根据字体的大小或是图片image大小给label一个合适的尺寸
 */
- (instancetype)initWithFrame:(CGRect)frame
{
    if (self = [super initWithFrame:frame]) {

        self.userInteractionEnabled = NO;

        [self setBackgroundImage:[UIImage imageNamed:@"main_badge"] forState:UIControlStateNormal];

        // 设置字体大小
        self.titleLabel.font = CZBadgeViewFont;

        [self sizeToFit];

    }
    return self;
}

/*
 1:计算UILable的根据字体的大小计算出lable的size

 NSDictionary *attribute = @{NSFontAttributeName: [UIFont systemFontOfSize:13]};
 CGSize size = [@“相关NSString” boundingRectWithSize:CGSizeMake(100, 0) options: NSStringDrawingTruncatesLastVisibleLine | NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading attributes:attribute context:nil].size;

此方法一般是宽度给定,高度自适应,第一个参数,可参考的矩形宽度,第二个参数为绘制矩形框的时候参数,第三个参数,字体的样式,第四个可为nil,返回的是一个CGSIZE类型

 NSStringDrawingTruncatesLastVisibleLine如果文本内容超出指定的矩形限制,文本将被截去并在最后一个字符后加上省略号。
 如果没有指定NSStringDrawingUsesLineFragmentOrigin选项,则该选项被忽略。

 NSStringDrawingUsesLineFragmentOrigin: 绘制文本时使用 line fragement origin 而不是 baseline origin。

 NSStringDrawingUsesFontLeading:

 计算行高时使用行距。(译者注:字体大小+行间距=行距)

 NSStringDrawingUsesDeviceMetrics:

 计算布局时使用图元字形(而不是印刷字体)。

 Use the image glyph bounds (instead of the typographic bounds) when computing layout.
 其中如果options参数为NSStringDrawingUsesLineFragmentOrigin,那么整个文本将以每行组成的矩形为单位计算整个文本的尺寸。(在这里有点奇怪,因为字体高度大概是13.8,textView中大概有10行文字,此时用该选项计算出来的只有5行,即高度为69,而同时使用NSStringDrawingUsesFontLeading | NSStringDrawingUsesLineFragmentOrigin却可以得出文字刚好有10行,即高度为138,这里要等iOS7官方的文档出来再看看选项的说明,因为毕竟以上文档是iOS6的东西)

 如果为NSStringDrawingTruncatesLastVisibleLine或者NSStringDrawingUsesDeviceMetric,那么计算文本尺寸时将以每个字或字形为单位来计算。

 如果为NSStringDrawingUsesFontLeading则以字体间的行距(leading,行距:从一行文字的底部到另一行文字底部的间距。)来计算。

 各个参数是可以组合使用的,如NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingTruncatesLastVisibleLine。

 */
- (void)setBadgeValue:(NSString *)badgeValue
{
    _badgeValue = badgeValue;

    // 判断badgeValue是否有内容
    if (badgeValue.length == 0 || [badgeValue isEqualToString:@"0"]) { // 没有内容或者空字符串,等于0
        self.hidden = YES;
    }else{
        self.hidden = NO;
    }

   CGSize size = [badgeValue sizeWithFont:CZBadgeViewFont];

    if (size.width > self.width) { // 文字的尺寸大于控件的宽度
        [self setImage:[UIImage imageNamed:@"new_dot"] forState:UIControlStateNormal];
        [self setTitle:nil forState:UIControlStateNormal];
        [self setBackgroundImage:nil forState:UIControlStateNormal];
    }else{
        [self setBackgroundImage:[UIImage imageNamed:@"main_badge"] forState:UIControlStateNormal];
        [self setTitle:badgeValue forState:UIControlStateNormal];
        [self setImage:nil forState:UIControlStateNormal];
    }

}

5: CZNavigationController 内部封装的代码

#import "UIBarButtonItem+Item.h"
#import "CZNavigationController.h"

#import "CZTabBar.h"

@interface CZNavigationController ()<UINavigationControllerDelegate>

@property (nonatomic, strong) id popDelegate;

@end

@implementation CZNavigationController

+ (void)initialize
{
    // 获取当前类下面的UIBarButtonItem
    UIBarButtonItem *item = [UIBarButtonItem appearanceWhenContainedIn:self, nil];

    // 设置导航条按钮的文字颜色
    NSMutableDictionary *titleAttr = [NSMutableDictionary dictionary];
    titleAttr[NSForegroundColorAttributeName] = [UIColor orangeColor];
    [item setTitleTextAttributes:titleAttr forState:UIControlStateNormal];

    // 注意导航条上按钮不可能,用模型的文字属性设置是不好使
//    // 设置不可用
//    titleAttr = [NSMutableDictionary dictionary];
//    titleAttr[NSForegroundColorAttributeName] = [UIColor lightGrayColor];
//    [item setTitleTextAttributes:titleAttr forState:UIControlStateDisabled];

}

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    _popDelegate = self.interactivePopGestureRecognizer.delegate;
    self.delegate = self;
}

- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
{
    if (self.childViewControllers.count) { // 不是根控制器

        viewController.hidesBottomBarWhenPushed = YES;

        UIBarButtonItem *left = [UIBarButtonItem barButtonItemWithImage:[UIImage imageNamed:@"navigationbar_back"] highImage:[UIImage imageNamed:@"navigationbar_back_highlighted"] target:self action:@selector(popToPre) forControlEvents:UIControlEventTouchUpInside];
        // 设置导航条的按钮
        viewController.navigationItem.leftBarButtonItem = left;

        UIBarButtonItem *right = [UIBarButtonItem barButtonItemWithImage:[UIImage imageNamed:@"navigationbar_more"] highImage:[UIImage imageNamed:@"navigationbar_more_highlighted"] target:self action:@selector(popToRoot) forControlEvents:UIControlEventTouchUpInside];
        viewController.navigationItem.rightBarButtonItem = right;
    }

    [super pushViewController:viewController animated:animated];

}

- (void)popToRoot
{
    [self popToRootViewControllerAnimated:YES];
}
- (void)popToPre
{
    [self popViewControllerAnimated:YES];
}
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
    UITabBarController *tabBarVc = (UITabBarController *)[UIApplication sharedApplication].keyWindow.rootViewController;

    // 删除系统自带的tabBarButton
    for (UIView *tabBarButton in tabBarVc.tabBar.subviews) {
        if (![tabBarButton isKindOfClass:[CZTabBar class]]) {
            [tabBarButton removeFromSuperview];
        }
    }

}

- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
    if (viewController == self.viewControllers[0]) { // 是根控制器

        self.interactivePopGestureRecognizer.delegate = nil;

    }else{ // 非根控制器
        self.interactivePopGestureRecognizer.delegate = _popDelegate;

    }
}

@end

6:UIBarButtonItem+Item.h的封装

#import <UIKit/UIKit.h>

@interface UIBarButtonItem (Item)

+ (UIBarButtonItem *)barButtonItemWithImage:(UIImage *)image highImage:(UIImage *)highImage target:(id)target action:(SEL)action forControlEvents:(UIControlEvents)controlEvents;

@end
#import "UIBarButtonItem+Item.h"

@implementation UIBarButtonItem (Item)
+ (UIBarButtonItem *)barButtonItemWithImage:(UIImage *)image highImage:(UIImage *)highImage target:(id)target action:(SEL)action forControlEvents:(UIControlEvents)controlEvents
{
    // btn
    UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
    [btn setBackgroundImage:image forState:UIControlStateNormal];
    [btn setBackgroundImage:highImage forState:UIControlStateHighlighted];
    [btn sizeToFit];

    [btn addTarget:target action:action forControlEvents:controlEvents];

    return  [[UIBarButtonItem alloc] initWithCustomView:btn];
}
@end
时间: 2024-07-31 05:07:46

利用UITabBarController搭建项目的主界面---各种封装的相关文章

利用UITabBarController搭建项目的主界面

一:UITabBarController的简单介绍: 1.使用步骤: (1)初始化UITabBarController (2)设置UIWindow的rootViewController为UITabBarController (3)创建相应的子控制器(viewcontroller)(让子控制器作为导航控制器的根视图控制器,最外层包装一层导航条) (4)把子控制器添加到UITabBarController 2:加入方式:(1)[tb addChildViewController:c1]; (2)[e

利用vue-cli搭建项目后的目录结构

npm install -g vue-cli vue init webpack my-project(项目名称)  后的目录结构: -----build         webpack配置相关 -----config webpack配置相关 -----node-modules   npm install 安装的依赖代码库 -----src             存放项目源码 -----static

主流界面搭建原理(类似百思不得姐主界面)

一.界面搭建 1.项目需求 主界面能左右滚动,还能上下滚动,点击按钮跳转界面 2.分析界面 点击按钮跳转界面可以自定义UITabBarCotroller来实现 左右滚动,可以利用scrollView来实现  或 UICollectionView 上下滚动,用tableView可以实现 3.选择实现方案 方案一:   UITabBarCotroller + scrollView + tableView + titleView(TabBar条) UIScrollView弊端:没有做离屏渲染优化 使用

ASP.NET MVC搭建项目后台UI框架—1、后台主框架

目录 ASP.NET MVC搭建项目后台UI框架-1.后台主框架 ASP.NET MVC搭建项目后台UI框架-2.菜单特效 ASP.NET MVC搭建项目后台UI框架-3.面板折叠和展开 ASP.NET MVC搭建项目后台UI框架-4.tab多页签支持 ASP.NET MVC搭建项目后台UI框架-5.Demo演示Controller和View的交互 ASP.NET MVC搭建项目后台UI框架-6.客户管理(添加.修改.查询.分页) ASP.NET MVC搭建项目后台UI框架-7.统计报表 ASP

抛开vue-cli 利用requireJS搭建一个vue项目

---恢复内容开始--- 现在学习vue都是利用脚手架vue-cli快速搭建一个项目,这是非常高效的方法,我是十分推荐的,但是有时候在没有互联网的情况,我们没办法在node环境下,敲一个nmp install XXX帮我们搭建系统.这边也遇到了这样的情况,记录一下过程,备忘. 采用的方式是利用requireJS搭建一个遵循AMD规则的vue项目.关于这一块,大家可以百度一下,查询相关资料熟悉requireJS的使用. 第一步,都是建立一个如下常规的简单的工程结构: ----lib 存放js库文件

[应用妹 --第二篇 主界面Tab搭建] android应用市场之快速开发

主界面布局如下 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:la

【边做项目边学Android】手机安全卫士05_1:程序主界面

主界面布局(知识点:GridView) mainscreen.xml: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height=

利用tomcat搭建jsp开源内容管理系统

一.简介 Tomcat 服务器是一个免费的开放源代码的Web 应用服务器,属于轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP 程序的首选.对于一个初学者来说,可以这样认为,当在一台机器上配置好Apache 服务器,可利用它响应对HTML(标准通用标记语言下的一个应用.)页面的访问请求.实际上Tomcat 部分是Apache 服务器的扩展,但它是独立运行的,所以当你运行tomcat 时,它实际上作为一个与Apache 独立的进程单独运行的. 诀窍是,当配

日积(Running)月累(ZSSURE):WCF学习之“通过事件绑定控制WinForm宿主程序主界面控件”

背景: WCF服务需要寄宿到相应的可运行进程中执行,常见的有四种寄宿,分别是控制台程序.WinForm程序.IIS和Windows服务.之前学习老A博客和<WCF全面解析>时最常用到的是控制台寄宿,近期由于项目需求,需要在WinForm程序中调用WCF服务,本博文通过一个简单的实例来演示WCF在WinForm中的寄宿.并着重介绍如何利用事件绑定控制宿主主UI界面控件. 题记: 之前一直坚守在C++阵地,对于新语言.新技术(诸如Python.J2EE.Bigdata.AI)不甚感冒.自以为&qu