Если вы решите игнорировать то, что Apple говорит о том, что поддерживается (с помощью пользовательского подкласса UITabBar с Interface Builder и только вместе с ним), вот грязное решение (которое работает):
Это требует слабых знаний осреда выполнения ObjC, потому что мы собираемся перебирать вещи вокруг ... По сути, проблема в том, что я не могу заставить UITabBarController создать экземпляр класса, который я хочу создать (здесь MyCustomTabBarSubclass).Вместо этого он всегда создает экземпляр UITabBar
.
Но я знаю, как он это создает: вызывая -[[UITabBar alloc] initWithFrame:]
.И я также знаю, что всем функциям, принадлежащим семейству init
, разрешено возвращать либо экземпляр своего класса, либо подкласса (это основа кластеров классов).
Итак, я собираюсь использовать это.Я собираюсь изобразить (= заменить реализацию) метода -initWithFrame:
UITabBar моей собственной версией, которая вместо вызова up (self = [super initWithFrame:]
) будет вызывать "down" (self = [MyCustomTabBarSubclass.alloc initWithFrame:]
).Таким образом, возвращаемый объект будет иметь класс MyCustomTabBarSubclass, чего я и пытаюсь достичь.
Обратите внимание, как я звоню MyCustomTabBarSubclass.alloc
- это потому, что у моего подкласса потенциально есть ивары, которых нет у UITabBar, таким образом, увеличивая размер памяти.Возможно, мне придется освободить себя перед тем, как перераспределить его, иначе я мог бы вытечь выделенную память, но я совсем не уверен (а ARC «запрещает» мне делать вызов -release
, поэтому мне пришлось бы сделать еще один шагобмана, чтобы назвать это).
EDIT
(Во-первых, этот метод также будет работать для любого случая, когда используются пользовательские классы 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-side:
@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, не забудьте вызвать код ObjC, который выполняет Swizzling.
Вот и все!Вы обманули UITabBarController в создании своего собственного подкласса UITabBar, а не ванильного.Если вы работаете в чистом ObjC, все становится еще проще (не связывайтесь с мостовыми заголовками, тема, которую я здесь не освещал).
Обязательный ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: Работа с ObjectiveCэто явно не то, что нужно делать легко.Убедитесь, что у вас нет лучшего решения - ИМХО, лучше использовать XIB только для того, чтобы избежать подобных ошибок, чем реализовать мое предложение.
Пример проблемы, которая может возникнуть: если вы используете в приложении несколько панелей вкладок, возможно, вы не захотите, чтобы все они были экземплярами MyCustomTabBarSubclass.Использование приведенного выше кода без изменений приведет к тому, что все панели вкладок станут экземплярами MyCustomTabBarSubclass, поэтому вам нужно будет найти способ указать __Swizzle_InitWithFrame
напрямую вызывать исходную реализацию или нет.