仿制新浪微博iOS客户端之三-自定义TabBar

  继续上一篇文章的进度,我们实际完成了微博基本框架的搭建,具体实现的效果如下左图,但我们实际需要实现的效果为右图,除去主要的页面内容不谈,仅仅下面的TabBar距离我们的需求就有相当的差距。因此本文着重于实现需要的效果。

                            

  再简要汇总一下我们的需求:

  1.我们要在TabBar原有四个按钮的基础上,再增加一个按钮,作为撰写微博的入口;

  2.新加入的按钮必须和原有按钮一起,均匀分布在TabBar上;

  3.新加入的按钮只有图片,没有文字。

  需求汇总如上,下面我们先进行一下分析和测试:

  1.在TabBarController上直接拖拽一个UITabBarItem,经测试,无法拖上,此方式无法实现需求;

  2.新创建ViewController,拖拽一个UITabBar,作为测试,直接修改默认的两个TabBarItem中的其中一个,测试结果如下: 

  使用此种方式后,TabBarItem的图片完全无法正常显示,并且图片位置偏上,与要求不符;并且这种方式将改变原有的界面架构,因此这种方案也被放弃;

  以上两种方案均不能达到效果,也就意味着,要实现想要的效果,就只能使用最后一个办法:自定义TabBar。

一、自定义控件

  在iOS中,只要是能在屏幕上显示的控件,那就可以进行自定义。因此,当系统提供的控件类型无法满足使用需要时,使用自定义控件就是最好、最快捷的实现方式。

二、自定义TabBar

  方案确定后,立刻开始实施。

  创建UITabBar子类MainTabBar,并设置TabBarController中TabBar继承的类为MainTabBar如下:

  自此,设置的部分完毕,开始进入代码:

  1.创建按钮并添加到TabBar中。

  要往TabBar中添加按钮,首先就要创建按钮。

 1     /// 懒加载撰写按钮,设置图片并添加到TabBar中
 2     lazy var composeButton: UIButton = {
 3         let btn = UIButton()
 4         btn.setImage(UIImage(named: "tabbar_compose_icon_add"), forState: UIControlState.Normal)
 5         btn.setImage(UIImage(named: "tabbar_compose_icon_add_highlighted"), forState: UIControlState.Highlighted)
 6         btn.setBackgroundImage(UIImage(named: "tabbar_compose_button"), forState: UIControlState.Normal)
 7         btn.setBackgroundImage(UIImage(named: "tabbar_compose_button_highlighted"), forState: UIControlState.Highlighted)
 8         // 添加到父控件中
 9         self.addSubview(btn)
10         
11         return btn
12     }()

  2.调整按钮布局

  仅仅添加按钮,只能证明在逻辑上,TabBar有一个按钮子控件,但是并不足以显示到屏幕上,如果要显示出来,必须有准确的位置和尺寸才行。

  对于有子控件的控件来说,每一次对子控件位置的调整都会调用其自身的 layoutSubviews 方法,因此我们可以先在这个方法中测试一番,为后面的工作做准备。实际测试的结果如下:

  我们可以发现,在程序运行之后,layoutSubviews 方法被调用了两次,并且每次都打印出了6个子控件,除去对应四个子视图控制器的按钮外,还有另外两个控件,并且每一个控件都有详细的 位置、尺寸等信息。

  但是我们只需要关注其中的四个按钮,原因是我们仅需要对按钮的位置进行调整,再在中间位置插入我们创建的按钮即可。而这四个按钮相对于另外两个控件最大的区别是这四个按钮是响应者类型,而另外两个控件却不是,从它们的 userInteractionEnabled = NO 就可以看得出来。

  初始代码和实现效果分别如左、右图:

                        

  这事我们发现应该居于右侧的两个按钮也靠到了左边,因此需要再做调整,并加上对中央撰写按钮的尺寸设置。最终实现如下:

    private let buttonCount = 5
    
    override func layoutSubviews() {
        super.layoutSubviews()
        
        /// 设置每个按钮的宽高和基准尺寸
        let w = self.bounds.width / CGFloat(buttonCount)
        let h = self.bounds.height
        let frame = CGRectMake(0, 0, w, h)
        
        var index: CGFloat = 0
        for view in self.subviews as! [UIView] {
            if (view is UIControl && !(view is UIButton)) {
                /**
                *  当前控件是响应者对象,并且不是 UIButton 类型时参照设置尺寸位置
                */
                view.frame = CGRectOffset(frame, index * w, 0)
                index += (index == 1) ? 2 : 1
            }
        }
        /**
        *  设置撰写按钮的尺寸和位置
        */
        self.composeButton.frame = frame
        self.composeButton.center = CGPointMake(self.bounds.width * 0.5, self.bounds.height * 0.5 )
    }

  3.设置撰写按钮的点击事件

  撰写按钮将在后续执行相应的操作,因此也是需要响应事件的,我们在这里就为撰写按钮添加点击事件。

  回到最初始的 MainTabBarController 类中,调整 viewDidLoad 方法,给撰写按钮添加点击事件,同时实现点击事件所调用的方法,并测试如下:

  至此,自定义TabBar的工作全部完成。

 

  杂谈:

  1.开发思路

  在实际投入开发之前,开发人员通常并不能十分的确定应当使用何种方式或者手段完成开发任务,因此在伴随开发过程中,开发人员通常是边尝试边开发,不同的尝试各种方案,最后找出可以实现的方案(也有可能此时找到的方案并不是最佳的)。因此在我们平时的开发中,对于独立性比较强或者独立性较强的功能或者模块,可以采用在另一个项目中测试、完成、实现,然后再移植到正常开发的代码中的模式。这种方式也是对正常开发影响最小的一种方法。

  有的开发者在收到需求后,喜欢找一个自己认为可能实现的方式,然后进行开发,并寄希望一次运行调试成功,如果遇到了问题,一旦原本的方案走不通,便有可能放弃,重新开始。而实际上,遇到bug和问题是开发人员的家常便饭,因此,这种方式起始并不可取。在这里我建议 “还没有能够久经考验的开发模式” 的开发人员遵循“从需求倒推”的开发方式和思路。

  在苹果目前提供的解决方案中,除去非常尖端的问题无法实现,其他的那些所谓“高大上”的难题都是可以解决的,前提是能找到合适的解决方案,除了在第一点中所说的多尝试,更重要的是利用倒推的方法,可以逐步理清方案中所使用的技术点和思路,这也是我推荐这个方式的最重要的原因。

  2.打印调试信息

  在开发中,开发者通常使用打印调试信息的方式来判断代码逻辑是否正确。但由于目前 Swift 语言的输出还不够智能,因此打印出来的结果通常都直接串连在一起。而 OC 和 Swift 之间有着良好的互通性,并且 OC 在打印调试信息时的格式是非常干净和整齐的,因此在打印 Swift 的调试信息时,可以将 Swift 对象转换为 OC 对象,以获得更清晰、明确的结果。

 

posted on 2015-06-06 23:36  Tieria  阅读(2254)  评论(0编辑  收藏  举报

导航