UINavigationBar
有一个小问题,我正在努力解决。当您在 View Controller 中使用 prefersStatusBarHidden()
方法隐藏状态栏时(我不想禁用整个应用程序的状态栏),导航栏失去其高度的 20pt 属于状态栏。基本上导航栏缩小了。
我尝试了不同的解决方法,但我发现每一种方法都有缺点。然后我遇到了这个 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()
方法被注释掉了,因为离开它会给我一个方法重新声明错误。
我之前没有在 Swift 或 Objective-C 中做过方法调配,所以我不确定解决这个问题的下一步,甚至根本不可能。
有人可以解释一下吗?
谢谢。
更新 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:"))
}
}
更新 2:Jasper 帮助我实现了所需的功能,但显然它有几个主要缺点。
由于扩展中的 load()
方法未触发,正如 Jasper 所建议的,我将以下代码块移至应用程序委托(delegate)的 didFinishLaunchingWithOptions
方法。
method_exchangeImplementations(class_getInstanceMethod(UINavigationBar.classForCoder(), "sizeThatFits:"), class_getInstanceMethod(UINavigationBar.classForCoder(), "sizeThatFits_FixedHeightWhenStatusBarHidden:"))
而且我不得不在 fixedHeightWhenStatusBarHidden
属性的 getter 中将返回值硬编码为“true”,因为现在 swizzling 代码在应用程序委托(delegate)中执行,您无法从 View 中设置值 Controller 。这使得扩展在可重用性方面变得多余。
所以这个问题或多或少还是悬而未决的。如果有人有改进的想法,请回答。
最佳答案
使用动态调度的 Objective-C 支持以下内容:
类方法调配:
您在上面使用的那种。一个类的所有实例都将用新的实现替换它们的方法。新的实现可以选择性地包装旧的。
Isa 指针调配:
一个类的实例被设置为一个新的运行时生成的子类。这个实例的方法将被替换为一个新的方法,它可以选择包装现有的方法。
消息转发:
在将消息转发给另一个处理程序之前,一个类通过执行一些工作充当另一个类的代理。
这些都是强大的拦截模式的变体,许多 Cocoa 的最佳功能都依赖于它。
输入 swift :
Swift 延续了 ARC 的传统,因为编译器将代表您进行强大的优化。它将尝试内联您的方法或使用静态分派(dispatch)或 vtable 分派(dispatch)。虽然速度更快,但这些都可以防止方法拦截(在没有虚拟机的情况下)。但是,您可以通过遵守以下内容向 Swift 表明您想要动态绑定(bind)(以及方法拦截):
- 通过扩展 NSObject 或使用 @objc 指令。
- 通过将动态属性添加到函数,例如
public dynamic func foobar() -> AnyObject
在您上面提供的示例中,满足了这些要求。您的类是通过UIView
和UIResponder
从NSObject
传递派生的,但是有该类别有些奇怪:
- 该类别正在覆盖加载方法,该方法通常会为一个类调用一次。从类别中执行此操作可能并不明智,而且我猜测虽然它以前可能有效,但在 Swift 的情况下却没有。
尝试将 Swizzling 代码移动到您的 AppDelegate 的 did finish launching 中:
//Put this instead in AppDelegate
method_exchangeImplementations(
class_getInstanceMethod(UINavigationBar.self, "sizeThatFits:"),
class_getInstanceMethod(UINavigationBar.self, "sizeThatFits_FixedHeightWhenStatusBarHidden:"))
关于ios - Swift 中的方法调配,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25651081/