Hello World
Spiga

抗拒“组合”的UITabBarController

2010-12-13 13:38 by 老赵, 7913 visits

最近在写一个iPhone应用程序,基于MonoTouch,所以在开发方面的问题,基本都是在界面元素的搭建上。这个程序界面相对比较复杂,于是我根据自己的想法来进行组合,结果发现UITabBarController不能放入其他的视图内,而只能直接放在Window上(或Window里的UINavigationController里),否则就会出现界面向下偏移的情况。现在虽然有workaround,但是对于UITabBarController抗拒组合的情况,只能深表叹息了。

先来看一下这个界面的最终效果吧:

这里再简单描述一下效果。打开程序以后看到的是一个标准的带有两个标签界面,目前选中第一个,界面上有一个按钮。点击按钮,整个界面(连同下方的标签栏)都会切换至新界面,并可以退回。在第二个标签内则有另一个按钮,点击则会切换至新一级,注意此时下方的标签栏会保持不变。不过这个程序最关键的一点在于,标签栏上放浮动着一个文字区域,它独立于标签栏中的各个视图。

显然这里会需要一个UINavigationController作为根元素,其中放入一个UITabBarController和文字区域。在UITabBarController的第二个标签中,则放入另一个UINavigationController。由于“根导航”在切换时,UITabBarController和文字区域要同时显示和消失,于是我很自然地打算将UITabBarController和一个UILabel组合成一个自定义的UIViewController。这样的界面本该很容易,因为控件的组合是编写界面的常用手段。

于是我新建了一个CustomController.xib文件,放入了一个UITabBarController和一个UILabel,并补充了一些操作逻辑(显示拖动时的坐标)。这些文字显示的逻辑本就属于CustomController,在实际应用中它也会和UITabBarController内部的视图产生交互,因此我把这些逻辑都隐藏在CustomController中。我认为这个组合方式十分合理。不过,在界面上显示MyTabBarController后则出现了奇怪的状况:

当然,上面这个只是我直接把一个UITabBarController的View放入Window里的UIView控件之后出现的情况:向下整体偏移了。这个偏移量是最上方状态栏的高度,这让我很摸不着头脑。经过了一整下午的纠结试验,我的结论是:似乎UITabBarController抗拒组合。说地具体一些:UITabBarController会把视图中的UITabBar控件定位在屏幕下方,但是在计算位置的时候,它不会关注自己父容器,而是茫然地认为自己一定是在根窗体上,于是会把顶部状态栏的高度考虑进去。于是,如果它的父视图不是从整个界面(包括状态栏)的顶部算起,UITabBar的位置便会出现偏移了。

有资深iOS开发者告诉我,我把UITabBarController组合到另一个UIViewController里不是UITabBarController的标准用法,如果我要组合,就应该使用UITabBar控件自己搭配,自己处理切换逻辑。不过我始终认为UI元素(不单指控件)应该意识到自己会参与组合。例如UITableViewController,UIImagePickerController,组合使用是它们的天然职责。在我看来,如其他(我用过的)UI库一般,只要为每个控件设好Dock(类似iOS里的Auto Resizing),组合起来应该是随意自然的。

既然不能组合,那么扩展的方法似乎只有继承了——这样我便不能使用Interface Builder绘制界面,麻烦了不少。这里我创建一个MyTabBarController继承UITabBarController,并补充一些逻辑:

public class MyTabBarController : UITabBarController
{
    /* Constructors */

    public override void ViewDidLoad()
    {
        base.ViewDidLoad();

        this.m_label = new UILabel()
        {
            Text = "Hello MonoTouch",
            TextAlignment = UITextAlignment.Center,
            Frame = new System.Drawing.RectangleF(0, 400, 320, 20),
            AutoresizingMask = UIViewAutoresizing.FlexibleTopMargin
        };

        this.View.AddSubview(this.m_label);
        this.View.BringSubviewToFront(this.m_label);
    }

    private UILabel m_label;

    public override void TouchesMoved(NSSet touches, UIEvent evt)
    {
        base.TouchesMoved(touches, evt);

        var touch = (UITouch)touches.AnyObject;
        var location = touch.LocationInView(this.View);
        this.m_label.Text = location.ToString();
    }
}

这样,就差不多了,剩下的就是简单地嵌套关系,以及在切换到第二个Tab的时候隐藏“根导航”的导航栏。

如果您有更好的做法,请务必告诉我。

Creative Commons License

本文基于署名 2.5 中国大陆许可协议发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名赵劼(包含链接),具体操作方式可参考此处。如您有任何疑问或者授权方面的协商,请给我留言

Add your comment

19 条回复

  1. 老赵
    admin
    链接

    老赵 2010-12-14 17:12:11

    我一不小心把评论都删了……

  2. 链接

    2010-12-15 10:05:17

    outlook的rss订阅不了 老赵 有什么建议不吖

  3. 老赵
    admin
    链接

    老赵 2010-12-15 13:53:02

    @彬

    我不知道为什么订阅不了……

  4. Star
    61.160.111.*
    链接

    Star 2010-12-15 16:10:48

    请问老赵大牛一个无关本主题的问题,string a="b"和string str="c"+"d"各自分配的几次内存,刚开始学习IL,不是很明白,还请指教

  5. 老赵
    admin
    链接

    老赵 2010-12-15 16:36:06

    @Star

    就“b”,“c”,“d”,“cd”各一个吧,话说这个和IL其实关系不大。

  6. Star
    61.160.111.*
    链接

    Star 2010-12-15 16:46:08

    我一直也是这么想的,可最近在看anytao的相关blog,觉的string a的时候已经在线程栈上开辟了内存用来存放托管堆中"b"内存的引用,所以觉的光string a="b"就应该两次内存了,不知道是不是这样,觉的挺模糊的,说错了还望见谅

  7. waynebaby
    210.22.108.*
    链接

    waynebaby 2010-12-15 17:23:28

    引用应该不算分配的内存吧 因为都是常量字符串编译期应该都加载到intern中了

    另外 "c" +"d" 编译器应该会直接编译成"cd" 相当于 string str="cd"

    所以我总觉得是 "b" 一次 "cd"一次的说?

    睡眠不足糊涂中

  8. waynebaby
    210.22.108.*
    链接

    waynebaby 2010-12-15 17:26:47

    严格的说 从intern中获取引用也不算分配内存吧。。。 也就是说一次都没有?(·&#(!&·#·#

  9. Star
    61.160.111.*
    链接

    Star 2010-12-15 17:35:43

    刚用IL又看了下,现在支持waynebaby的("b"一次"cd"一次)观点,老赵牛牛,看你怎么说啦:)

  10. 老赵
    admin
    链接

    老赵 2010-12-15 17:49:01

    @Star

    哦哦,"c" + "d"被编译器优化掉了,那么就是“b”和“cd”。

  11. chenkai
    124.207.144.*
    链接

    chenkai 2010-12-16 18:16:21

    MFC IPhone开发基于MAC苹果机. 但针对Object-C语言还在不断适应. IPhone 这种坚决不开源的态度 一直没有转变...

  12. hai_blue
    122.96.60.*
    链接

    hai_blue 2010-12-28 14:09:01

    UITabBarController 和 UINavigationController 是怎么结合的啊? 非常想看一下 第二个标签的代码。。。

  13. mlkimg
    183.33.238.*
    链接

    mlkimg 2011-01-10 12:44:42

    UITabBarController跟UINavigationController的组合方式只能是UINavigationController放到UITabBarController里面作为UITabBarController的Root View Controller

  14. Allen Hsu
    114.94.101.*
    链接

    Allen Hsu 2011-01-14 16:21:57

    个人觉得这个问题没有那么复杂,试试下面两个方法:
    1、在对应的 ViewController 中设置 self.wantsFullScreenLayout = YES。
    2、在 xib 中设置 statusbarstyle 为 none;

  15. peerben
    180.168.123.*
    链接

    peerben 2011-05-20 15:45:26

    我对这个问题的解决方法是:

    [self.windows addsubview:tabbarcontroller.view]; [self.windows addsubview:logincontroller.view];

    LoginViewContoller.m

    -(IBAction)loginToXXX{ [self.view removeFromSuperview]; }

    博主可否有新浪微博,我想follow你,我的微博名peerben,我好像在twitter上follow的你,不过好久没登了。

  16. 老赵
    admin
    链接

    老赵 2011-05-21 21:10:53

    @peerben

    我在新浪微博上就是@老赵。

  17. newsky
    121.236.178.*
    链接

    newsky 2011-08-05 17:25:13

    刚买399美元买了个MonoTouch,但因为没苹果开发者账户,没法真机调试

  18. Lynn
    183.17.193.*
    链接

    Lynn 2011-08-22 13:49:12

    我是个使用monotouch的新手,想知道如何将NavigationController 添加到TabBarController的一个view中?谢谢

  19. jonesduan
    116.205.179.*
    链接

    jonesduan 2012-02-29 00:32:05

    大牛,您的思想我很清楚了,但是我在实际动手做的过程中,在切换到第二个item的时候,隐藏根NavigationController的navigationBar出现点儿小问题:

    效果是:启动应用的时候,第一次切换到第二个tabBarItem的时候,子NavigationController的navigationBar会向上偏移一个StatusBar的高度就是上面显示电池电量的那个StatusBar,移出的部分显示白条,仅仅是启动应用后第一次切换到第二个tabBarItem会出现这样的情况,后面切换到第一个item再切换回第二个item就不会了,这个是什么原因呢???

    具体我的隐藏根NavigationBar的做法见链接:http://topic.csdn.net/u/20120228/23/b5c334d0-8f8c-4726-af4b-dc42028b4ac6.html?21575

    很感谢回复我,同时也很想了解大牛的实现方法是怎样的,可以demo发我邮箱:lengxuewuqingg@126.com,如果可以的话~!我是鸽子的同事~!^_^

发表回复

登录 / 登录并记住我 ,登陆后便可删除或修改已发表的评论 (请注意保留评论内容)

昵称:(必填)

邮箱:(必填,仅用于Gavatar

主页:(可选)

评论内容(大于5个字符):

  1. Your Name yyyy-MM-dd HH:mm:ss

使用Live Messenger联系我