Существует небольшая проблема с UINavigationBar
, которую я пытаюсь преодолеть. Когда вы скрываете строку состояния с помощью метода prefersStatusBarHidden()
в контроллере вида (я не хочу отключать строку состояния для всего приложения), навигационная панель теряет 20pt от ее высоты, которая принадлежит строке состояния. В основном панель навигации сжимается.Метод swizzling in Swift
Я пробуя различные обходные пути, но я обнаружил, что каждый из них имел свои недостатки. Затем я наткнулся на этот category, который, используя метод swizzling, добавляет свойство fixedHeightWhenStatusBarHidden
в класс UINavigationBar
, который решает эту проблему. Я тестировал его в Objective-C, и он работает. Вот header и implementation исходного кода Objective-C.
Теперь, когда я делаю свое приложение в Swift, я попытался перевести его в Swift.
Первой проблемой, с которой я столкнулся, является расширение Swift, которое не может храниться в свойствах. Поэтому мне пришлось рассчитывать на вычисленное свойство, чтобы объявить свойство fixedHeightWhenStatusBarHidden
, которое позволяет мне установить значение. Но это еще одна проблема. По-видимому, вы не можете назначать значения для вычисляемых свойств. Вот так.
self.navigationController?.navigationBar.fixedHeightWhenStatusBarHidden = true
Я получаю ошибку Невозможно присвоить результат этого выражения.
В любом случае ниже мой код. Он компилируется без каких-либо ошибок, но он не работает.
import Foundation
import UIKit
extension UINavigationBar {
var fixedHeightWhenStatusBarHidden: Bool {
return objc_getAssociatedObject(self, "FixedNavigationBarSize").boolValue
}
func sizeThatFits_FixedHeightWhenStatusBarHidden(size: CGSize) -> CGSize {
if UIApplication.sharedApplication().statusBarHidden && fixedHeightWhenStatusBarHidden {
let newSize = CGSizeMake(self.frame.size.width, 64)
return newSize
} else {
return sizeThatFits_FixedHeightWhenStatusBarHidden(size)
}
}
/*
func fixedHeightWhenStatusBarHidden() -> Bool {
return objc_getAssociatedObject(self, "FixedNavigationBarSize").boolValue
}
*/
func setFixedHeightWhenStatusBarHidden(fixedHeightWhenStatusBarHidden: Bool) {
objc_setAssociatedObject(self, "FixedNavigationBarSize", NSNumber(bool: fixedHeightWhenStatusBarHidden), UInt(OBJC_ASSOCIATION_RETAIN))
}
override public class func load() {
method_exchangeImplementations(class_getInstanceMethod(self, "sizeThatFits:"), class_getInstanceMethod(self, "sizeThatFits_FixedHeightWhenStatusBarHidden:"))
}
}
fixedHeightWhenStatusBarHidden()
метод в середине комментируется, потому что оставить его дает мне ошибку метод переопределение.
Я раньше не делал метод swizzling в Swift или Objective-C, поэтому я не уверен в следующем шаге, чтобы решить эту проблему или даже вообще это возможно.
Может кто-то пролить свет на это?
спасибо.
UPDATE 1: Благодаря newacct, была решена моя первая проблема о свойствах. Но код не работает. Я обнаружил, что выполнение не достигает метода load()
в расширении. Из комментариев в this ответ, я узнал, что либо ваш класс должен быть потомком NSObject
, что в моем случае, UINavigationBar
не имеет прямого потока от NSObject
, но он реализует NSObjectProtocol
. Поэтому я не уверен, почему это все еще не работает. Другой вариант - добавление @objc
, но Swift не позволяет добавлять его в расширения. Ниже приведен обновленный код.
Вопрос по-прежнему открыт.
import Foundation
import UIKit
let FixedNavigationBarSize = "FixedNavigationBarSize";
extension UINavigationBar {
var fixedHeightWhenStatusBarHidden: Bool {
get {
return objc_getAssociatedObject(self, FixedNavigationBarSize).boolValue
}
set(newValue) {
objc_setAssociatedObject(self, FixedNavigationBarSize, NSNumber(bool: newValue), UInt(OBJC_ASSOCIATION_RETAIN))
}
}
func sizeThatFits_FixedHeightWhenStatusBarHidden(size: CGSize) -> CGSize {
if UIApplication.sharedApplication().statusBarHidden && fixedHeightWhenStatusBarHidden {
let newSize = CGSizeMake(self.frame.size.width, 64)
return newSize
} else {
return sizeThatFits_FixedHeightWhenStatusBarHidden(size)
}
}
override public class func load() {
method_exchangeImplementations(class_getInstanceMethod(self, "sizeThatFits:"), class_getInstanceMethod(self, "sizeThatFits_FixedHeightWhenStatusBarHidden:"))
}
}
UPDATE 2: Джаспер помог мне достичь желаемой функциональности, но, видимо, речь идет с парой главных недостатков.
Поскольку метод load()
в расширении не стрелял, как предложил Джаспер, я переместил следующий код в метод делегатов didFinishLaunchingWithOptions
.
method_exchangeImplementations(class_getInstanceMethod(UINavigationBar.classForCoder(), "sizeThatFits:"), class_getInstanceMethod(UINavigationBar.classForCoder(), "sizeThatFits_FixedHeightWhenStatusBarHidden:"))
И я должен был жёстко возвращаемого значения «истина» в поглотителе из fixedHeightWhenStatusBarHidden
собственности, потому что теперь, когда код swizzling выполняет в приложении делегата, вы не можете установить значение из контроллера представления. Это делает избыточность избыточной, когда дело доходит до повторного использования.
Таким образом, вопрос по-прежнему открыт более или менее. Если у кого-то есть идея улучшить его, ответьте.
Почему вы swizzling 'UINavigationBar'? Вы можете сделать это легко с помощью пользовательского подкласса 'UINavigationBar'. Документы для ['UINavigationController'] (https://developer.apple.com/library/ios/documentation/UIKit/Reference/UINavigationController_Class/) содержат информацию об использовании пользовательского подкласса' UINavigationBar', как и [@ AlexPretzlav's answer] (http://stackoverflow.com/questions/25651081/method-swizzling-in-swift#26641469). –