方案一:UIEdgeInsets

适用场景:

  • 适合APP的TabBarItemImage的图片资源放在本地

  • 图片超出tabbar的高度,需移动其位置,来进行适应

弊端:

若在本地配置好后,tabbar的图片就不能改动了,若tabbar的图片来自服务端,且不停的切换图片的大小,以上则很难满足。若有此方面的需求请看方案二。

实现:

[tabbarItem setImageInsets:UIEdgeInsetsMake(<#CGFloat top#>, <#CGFloat left#>, <#CGFloat bottom#>, <#CGFloat right#>)]

注:图片太大超出tabbar时,系统并不会调整image和title的位置,你需要根据图片的高度,计算出需要往上移动的高度,然后设置top和bottom属性即可。切记top = - bottom,否则image将会被拉伸或者被压缩。

方案二:Runtime

利用runtime的话相对方案一来说要比较复杂一点,但其灵活度比较高,我们能够根据服务端所给的image来动态的变化TabBarItem的大小,类似像淘宝、京东活动时。思想:主要是利用runtime对UITabBar的layoutSubviews进行重写,然后调整UITabBarItem的位置。另外,当时在做的APP已经有4-5年的历史了,一开始打算自已定制tabbar,发现要改动的还是挺多的,于是就放弃了。做之前也看了前辈iOS程序犭袁的CYLTabBarController,从中也学到了不少思路。

实现:

  • 首先我们使用runtime method swizzling交换系统的- (void)layoutSubviews;

  • 使用KVC对系统的UITabBarButton、UITabBarSwappableImageView、UITabBarButtonLabel、_UIBadgeView进行捕获

  • 拿到控件后我们对其的frame进行计算,判断当前有没有超出tabbar的高度,若超出则进行处理

  • 再次利用runtime method swizzling交换系统的- (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event;使图片超过后也能接受点击

代码:

  • method swizzling:

static void ExchangedMethod(SEL originalSelector, SEL swizzledSelector, Class class) {
    Method originalMethod = class_getInstanceMethod(class, originalSelector);
    Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
    BOOL didAddMethod =
    class_addMethod(class,
                    originalSelector,
                    method_getImplementation(swizzledMethod),
                    method_getTypeEncoding(swizzledMethod));
    if (didAddMethod) {
        class_replaceMethod(class,
                     swizzledSelector,
                     method_getImplementation(originalMethod),
                     method_getTypeEncoding(originalMethod));
    }
    else {
        method_exchangeImplementations(originalMethod, swizzledMethod);
    }
}
  • 计算frame,并对其重新布局

UIView *tabBarImageView, *tabBarButtonLabel, *tabBarBadgeView;
        for (UIView *sTabBarItem in childView.subviews) {
            if ([sTabBarItem isKindOfClass:NSClassFromString(@"UITabBarSwappableImageView")]) {
                tabBarImageView = sTabBarItem;
            }
            else if ([sTabBarItem isKindOfClass:NSClassFromString(@"UITabBarButtonLabel")]) {
                tabBarButtonLabel = sTabBarItem;
            }
            else if ([sTabBarItem isKindOfClass:NSClassFromString(@"_UIBadgeView")]) {
                tabBarBadgeView = sTabBarItem;
            }
        }
        NSString *tabBarButtonLabelText = ((UILabel *)tabBarButtonLabel).text;
        CGFloat y = CGRectGetHeight(self.bounds) - (CGRectGetHeight(tabBarButtonLabel.bounds) + CGRectGetHeight(tabBarImageView.bounds));
        if (y < 3) {
            if (!tabBarButtonLabelText.length) {
                space -= tabBarButtonLabelHeight;
            }
            childView.frame = CGRectMake(childView.frame.origin.x,
                               y - space,
                               childView.frame.size.width,
                               childView.frame.size.height - y + space);
        }
  • 让图片超出部分也能响应点击事件

- (UIView *)s_hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    if (!self.clipsToBounds && !self.hidden && self.alpha > 0) {
        UIView *result = [super hitTest:point withEvent:event];
        if (result) {
            return result;
        }
        else {
            for (UIView *subview in self.subviews.reverseObjectEnumerator) {
                CGPoint subPoint = [subview convertPoint:point fromView:self];
                result = [subview hitTest:subPoint withEvent:event];
                if (result) {
                    return result;
                }
            }
        }
    }
    return nil;
}

注意事项

  • 在给tabbar设置图片的时候一定要设置图片的renderingMode,否则就会出现下图中图片丢失的现象

  • UITabBarButton被修改frame之后,仅有UITabBarSwappableImageView能够响应点击事件,不过我们能够在UITabBar的- (void)touchesBegan:(NSSet *)touches withEvent:(nullable UIEvent *)event;方法中捕获到

  • 当适配图片后不要忘记适配_UIBadgeView的frame

Runtime实战之定制TabBarItem大小的更多相关文章

  1. 包建强的培训课程(11):iOS Runtime实战

    @import url(http://i.cnblogs.com/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/c ...

  2. Spring Boot实战之定制URL匹配规则

    本文首发于个人网站:Spring Boot实战之定制URL匹配规则 构建web应用程序时,并不是所有的URL请求都遵循默认的规则.有时,我们希望RESTful URL匹配的时候包含定界符". ...

  3. Spring Boot实战之定制type Formatters

    本文首发于个人网站:Spring Boot实战之定制type Formatters 前面我们有篇文章介绍了PropertyEditors,是用来将文本类型转换成指定的Java类型,不过,考虑到Prop ...

  4. iOS:定制自适应大小的透明吐司弹框

    一.简单介绍 创建一个吐司消息的黑色透明弹框,可以根据消息长短自适应大小. 可以手动创建手动显示手动关闭,也可以手动创建自动显示自动关闭. 简单好用. 二.代码使用 .h文件 // // LiveHU ...

  5. Spring Boot实战之定制自己的starter

    本文首发于个人网站,原文地址:http://www.javaadu.online/?p=535,如需转载,请注明出处 在学习Spring Boot的过程中,接触最多的就是starter.可以认为sta ...

  6. 黑客攻防技术宝典web实战篇:定制攻击自动化习题

    猫宁!!! 参考链接:http://www.ituring.com.cn/book/885 随书答案. 1. 指出使用自动技巧在应用程序中枚举标识符时用到的 3 个标识符“触点”. (a) HTTP ...

  7. iOS 收藏的笔记

    目录 UI 资料类 网络篇 图表 动画 菜单栏 数据存储和数据库 第三方库 社交分享 刷新 视频音频 其他 阅读 JS 导航 系统 支付 书籍 工具类 完整项目收集 DEMO UI http://ww ...

  8. iOS 自定义UITabBar

    推荐一篇非常好的集成各种UITabBar的三方库 <点击这里直取demo> 另外一篇根据runtime定制了一款可以出轨的UITarBar <Runtime实战之定制TabBarIt ...

  9. iOS 开发-- Runtime 1小时入门教程

    1小时让你知道什么是Objective-C Runtime,并对它有一定的基本了解,可以在开发过程中运用自如. 三.Objective-C Runtime到底是什么东西? 简而言之,Objective ...

随机推荐

  1. ASP.NET知识总结(1.网络传输层)

    1.网络传输层 1应用层(HTTP.FTP.SMTP)报文Message 2传输层(TCP.UDP)报文段Segment,为运行在不同主机上的应用程序进程间提供数据 传输服务.通过套接字(Socket ...

  2. nginx部分命令

    启动nginx start nginx 停止nginx nginx -s stop 重启nginx nginx -s reload 查看版本信息 nginx -v 大写V是查看配置信息 查看nginx ...

  3. 工欲善其事-Maven介绍与使用

    Maven是什么? Maven是一个项目管理和综合工具.Maven提供了开发人员构建一个完整的生命周期框架.开发团队可以自动完成项目的基础工具建设,Maven使用标准的目录结构和默认构建生命周期. 在 ...

  4. xml schema xmlns xmlns:xsi xsi:schemaLocation targetnamespace

    先上一段xml文档 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="htt ...

  5. 关于在程序中 文件新生成 在用os.system()程序对新生成的文件处理 举个栗子 如下:

    print 'save to ',savedir+os.sep+d["FILE_NAME"]                ff = open(savedir+os.sep+d[& ...

  6. TDatabase TQuery TDataSource TDBGrid 设定方法

    TDatabase TQuery TDataSource TDBGrid object Database1: TDatabase AliasName = 'sfis' DatabaseName = ' ...

  7. PHP mysql基础操作

    mysql连接操作 //建立连接$con = mysql_connect('localhost', 'root', '123456');//判断是否连接成功if($con){ die('连接失败!'. ...

  8. 提高C#代码质量-规范

    [规范习惯]命名规范1-命名空间 使用<Company>.<Component>2-程序集不必与命名空间同名3-命名空间使用附复数4-避免与FCL的类型重名5-类型名称用名词6 ...

  9. C#中xml操作

    序列化成一个字符串: public static string XMLSerialize<T>(T entity) { StringBuilder buffer = new StringB ...

  10. Constructing Roads——F

    F. Constructing Roads There are N villages, which are numbered from 1 to N, and you should build som ...