使用IB(使用情节提要或XIB),可以通过在Identity Inspector->自定义类中编辑类名称来使UITabBarController轻松使用UITabBar的子类。
如何在不使用IB的情况下模仿IB的“自定义类”功能?
我尝试了以下操作(在UITabBarController的子类中):
var customTabBar = MyCustomTabBarSubclass()
override var tabBar: UITabBar {
return customTabBar
}
无济于事–显示选项卡栏,但空白。由于从覆盖的return
中super.tabBar
对var
的{{1}}可以解决此问题,因此没有其他问题。
我想,问题是我没有设置customTabBar
(框架,位置,将其添加到视图层次结构中),但是我想拥有UITabBarController loadView
(我认为那是不确定的),就像我设置其他UITabBar
一样。
想法?
答案 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)
我认为你不能。对于这个问题,我花了一些时间浏览UITabBar
和UITabBarController
的文档。
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
}
直接调用原始实现。