没有Interface Builder的自定义UITabBar子类

时间:2019-01-22 18:42:44

标签: ios swift uikit uitabbarcontroller uitabbar

使用IB(使用情节提要或XIB),可以通过在Identity Inspector->自定义类中编辑类名称来使UITabBarController轻松使用UITabBar的子类。

如何在不使用IB的情况下模仿IB的“自定义类”功能?

我尝试了以下操作(在UITabBarController的子类中):

var customTabBar = MyCustomTabBarSubclass()

override var tabBar: UITabBar {
    return customTabBar
}

无济于事–显示选项卡栏,但空白。由于从覆盖的returnsuper.tabBarvar的{​​{1}}可以解决此问题,因此没有其他问题。

我想,问题是我没有设置customTabBar(框架,位置,将其添加到视图层次结构中),但是我想拥有UITabBarController loadView (我认为那是不确定的),就像我设置其他UITabBar一样。

想法?

3 个答案:

答案 0 :(得分:2)

这行得通,但是您可能无法通过App Store审查,因为它设置了公共API不允许您设置的值。

在您的UITabBarController的子类“ initWithNibName:bundle:viewDidLoad中,添加以下内容:

MyCustomTabBar *customTabBar = [[MyCustomTabBar alloc] initWithFrame:CGRectZero];
customTabBar.delegate = self;
[self setValue:customTabBar forKey:@"tabBar"];

将其视为概念证明,而不是必须在生产应用中使用的东西,因为从技术上讲,它是使用私有setTabBar:方法。

答案 1 :(得分:1)

我认为你不能。对于这个问题,我花了一些时间浏览UITabBarUITabBarController的文档。

tabBar的{​​{1}}属性是只读的。

UITabBarController

此外,@available(iOS 3.0, *) open var tabBar: UITabBar { get } // Provided for -[UIActionSheet showFromTabBar:]. Attempting to modify the contents of the tab bar directly will throw an exception. 的{​​{3}}中指出,您不应操纵此类属性。

  

您永远不要尝试操纵UITabBar对象本身   存储在此属性中。如果尝试这样做,则标签栏视图   引发异常。为选项卡栏配置项目   界面,您应该分配一个或多个自定义视图   控制器添加到viewControllers属性。标签栏收集   您指定的视图控制器中所需的选项卡栏项。

     

此属性提供的选项卡栏视图仅用于情况   您想要使用show(from :)方法显示操作表的位置   的UIActionSheet类。

只想添加:但是与UITabBarController不同的是,对UITabBarController进行子类化可以让您使用UINavigationController的子类来初始化此类子类:

UINavigationBar

不幸的是,UINavigationController(navigationBarClass: <#T##AnyClass?#>, toolbarClass: <#T##AnyClass?#>) 没有这种初始化方法。

答案 2 :(得分:1)

如果您选择忽略Apple关于受支持内容的说法(仅将自定义UITabBar子类与Interface Builder一起使用,并且仅与之配合使用),这是一个肮脏的解决方案(有效):

它需要对ObjC运行时有一定的了解,因为我们要花很多时间...本质上,问题是我不能强迫UITabBarController实例化想要实例化的类(在这里,MyCustomTabBarSubclass) 。相反,它总是实例化UITabBar

但是我知道它是如何实例化的:通过调用-[[UITabBar alloc] initWithFrame:]。而且我也知道,属于init族的所有函数都可以返回其类的实例或子类的实例(这是类集群的基础)。

因此,我将使用它。我将使用其自定义版本来处理(=替换实现)UITabBar的-initWithFrame:方法,而不是调用(self = [super initWithFrame:])来调用“ down”({{1} }。因此,返回的对象将属于MyCustomTabBarSubclass类,这是我要实现的目标。

请注意我如何调用self = [MyCustomTabBarSubclass.alloc initWithFrame:] –这是因为我的子类可能具有UITabBar没有的ivars,因此使其内存布局更大。我可能必须先释放self,然后再重新分配它,否则我可能会泄漏分配的内存,但是我一点也不知道(并且ARC“禁止”我调用MyCustomTabBarSubclass.alloc,因此我必须使用招式的另一步)。


编辑

(首先,此方法在使用IB的自定义类的任何情况下也适用)。

还要注意,实现此功能需要编写ObjC代码,例如,Swift不允许我们调用-release –并非双关语。这是代码:

alloc

您还必须确保实际的毛刷操作仅执行一次(例如IMP originalImp = NULL; id __Swizzle_InitWithFrame(id self, SEL _cmd, CGRect frame) { Class c = NSClassFromString(@"MyBundleName.MyCustomTabBarSubclass"); self = [c alloc]; //so that we'll return an instance of MyCustomTabBarSubclass if (self) { id (*castedImp)(id, SEL, CGRect) = (id (*)(id, SEL, CGRect))originalImp; self = castedImp(self, _cmd, frame); //-[super initWithFrame:] } return self; } )。这实际上是令人毛骨悚然的代码:

dispatch_once

ObjC方面就是这样。 斯威夫特方面:

Method method = class_getInstanceMethod(NSClassFromString(@"UITabBar"), @selector(initWithFrame:));

IMP swizzleImp = (IMP)__Swizzle_InitWithFrame;
originalImp = method_setImplementation(method, swizzleImp);

在初始化UITabBarController之前,请不要忘记调用执行混淆的ObjC代码。

就是这样!您已经欺骗UITabBarController实例化了自己的UITabBar子类,而不是实例化的子类。如果您使用的是纯粹的ObjC,则事情会变得更加容易(不会弄乱桥接标题,这是我在此未涵盖的主题)。

强制性免责声明:使用ObjectiveC运行时显然不容易。确保您没有更好的解决方案–恕我直言,仅使用XIB来避免发生这种麻烦,是比实施我的建议更好的主意。

可能出现的问题的一个示例:如果您在应用程序中使用多个选项卡栏,则可能不希望它们全部都是MyCustomTabBarSubclass实例。使用上面的代码而未做任何修改将导致所有标签栏成为MyCustomTabBarSubclass的实例,因此您必须找到一种方法来告诉@objc class MyCustomTabBarSubclass: UITabBar { lazy var anIvar: Int = 0 //just a example obviously // don't forget to make all your ivars lazy or optional // because the initialisers WILL NOT BE CALLED, as we are // circumventing the Swift runtime normal init mechanism } 直接调用原始实现。