如果您选择无视 Apple 关于支持的内容(使用带有 Interface Builder 的自定义 UITabBar 子类,并且仅使用它),这是一个肮脏的解决方案(有效):
它需要对 ObjC 运行时有一定的了解,因为我们要调换一些东西......本质上,问题是我不能强制 UITabBarController 实例化我希望它实例化的类(这里是 MyCustomTabBarSubclass) .相反,它总是实例化UITabBar。
但我知道它是如何实例化它的:通过调用-[[UITabBar alloc] initWithFrame:]。而且我还知道属于init 系列的所有函数都可以返回其类的实例或子类的实例(这是类簇的基础)。
所以,我将使用它。我将用我的自定义版本来调整(=替换实现)UITabBar 的-initWithFrame: 方法,而不是调用(self = [super initWithFrame:]),而是调用“down”(self = [MyCustomTabBarSubclass.alloc initWithFrame:])。因此,返回的对象将属于 MyCustomTabBarSubclass 类,这是我想要实现的。
注意我如何调用MyCustomTabBarSubclass.alloc——这是因为我的子类可能有 UITabBar 没有的 ivars,因此它的内存布局更大。我可能必须在重新分配之前释放自我,否则我可能会泄漏分配的内存,但我完全不确定(ARC“禁止”我打电话给-release,所以我不得不使用另一个步骤叫它的诡计)。
编辑
(首先,这种方法也适用于任何使用 IB 的自定义类的情况)。
另外请注意,实现这一点需要编写 ObjC 代码,因为 Swift 不允许我们调用 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)。以下是实际运行的代码:
Method method = class_getInstanceMethod(NSClassFromString(@"UITabBar"), @selector(initWithFrame:));
IMP swizzleImp = (IMP)__Swizzle_InitWithFrame;
originalImp = method_setImplementation(method, swizzleImp);
这就是 ObjC 方面的内容。
Swift 端:
@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
}
在初始化 UITabBarController 之前,不要忘记调用执行 swizzling 的 ObjC 代码。
就是这样!您欺骗了 UITabBarController 实例化了您自己的 UITabBar 子类,而不是普通的子类。如果你在纯 ObjC 中工作,事情会更容易(不要弄乱桥接头,我在这里没有涉及的主题)。
强制性免责声明: 弄乱ObjectiveC 运行时显然不是一件容易的事。确保您没有更好的解决方案 - 恕我直言,仅出于避免此类修补的目的使用 XIB 比实施我的建议更好。
可能出现的问题示例:如果您在应用程序中使用多个标签栏,您可能不希望所有标签栏都是 MyCustomTabBarSubclass 实例。使用我上面的代码而不进行修改将导致 all 标签栏成为 MyCustomTabBarSubclass 的实例,因此您必须找到一种方法来告诉 __Swizzle_InitWithFrame 是否直接调用原始实现。