Isu*_*uru 10 objective-c ios swift method-swizzling
UINavigationBar我正试图克服一个小问题.当您prefersStatusBarHidden()在视图控制器中使用方法隐藏状态栏时(我不想禁用整个应用程序的状态栏),导航栏会丢失属于状态栏的高度的20pt.基本上导航栏缩小了.

我正在尝试不同的解决方法,但我发现它们中的每一个都有缺点.那时,我发现这次来到类别,其使用方法交叉混合,添加了一个名为属性fixedHeightWhenStatusBarHidden的UINavigationBar解决了这个问题类.我在Objective-C中进行了测试,但它确实有效.以下是原始Objective-C代码的标题和实现.
现在,因为我在Swift中使用我的应用程序,所以我尝试将其转换为Swift.
我遇到的第一个问题是Swift扩展无法存储属性.所以我不得不解决计算属性来声明fixedHeightWhenStatusBarHidden使我能够设置值的属性.但这引发了另一个问题.显然,您无法为计算属性赋值.像这样.
self.navigationController?.navigationBar.fixedHeightWhenStatusBarHidden = true
Run Code Online (Sandbox Code Playgroud)
我得到错误无法分配给这个表达式的结果.
无论如何,下面是我的代码.它编译没有任何错误,但它不起作用.
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:"))
}
}
Run Code Online (Sandbox Code Playgroud)
fixedHeightWhenStatusBarHidden() 中间的方法被注释掉了,因为它给了我一个方法重新声明错误.
我没有在Swift或Objective-C中进行方法调整,所以我不确定下一步要解决这个问题,甚至根本不可能.
有人可以对此有所了解吗?
谢谢.
更新1:感谢newacct,我关于属性的第一期问题已经解决.但是代码仍然不能正常工作.我发现执行没有到达load()扩展中的方法.根据这个答案中的评论,我了解到你的班级需要成为后代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:"))
}
}
Run Code Online (Sandbox Code Playgroud)
更新2: Jasper帮助我实现了所需的功能,但显然它带来了几个主要的缺点.
由于load()扩展中的方法没有触发,正如Jasper建议的那样,我将以下代码块移动到app delegates的didFinishLaunchingWithOptions方法.
method_exchangeImplementations(class_getInstanceMethod(UINavigationBar.classForCoder(), "sizeThatFits:"), class_getInstanceMethod(UINavigationBar.classForCoder(), "sizeThatFits_FixedHeightWhenStatusBarHidden:"))
Run Code Online (Sandbox Code Playgroud)
而且我必须在getter of fixedHeightWhenStatusBarHiddenproperty 中将返回值硬编码为'true',因为现在调用代码在app委托中执行,你不能从视图控制器设置一个值.这使得扩展在可重用性方面是多余的.
所以问题仍然或多或少地开放.如果有人有想法改进它,请回答.
Jas*_*ues 17
Objective-C,使用动态调度支持以下内容:
类方法调配:
你在上面使用的那种.类的所有实例都将使用新实现替换其方法.新的实现可以选择包装旧的.
Isa-pointer嗖嗖声:
将类的实例设置为新的运行时生成的子类.此实例的方法将替换为新方法,该方法可以选择性地包装现有方法.
消息转发:
在将消息转发给另一个处理程序之前,类通过执行某些工作充当另一个类的代理.
这些都是强大的拦截模式的变种,这是Cocoa的许多最佳功能所依赖的.
输入Swift:
Swift延续了ARC的传统,因为编译器将代表您进行强大的优化.它将尝试内联您的方法或使用静态调度或vtable调度.虽然速度更快,但这些都可以防止方法拦截(在没有虚拟机的情况下).但是,您可以通过遵守以下内容向Swift表明您希望动态绑定(以及方法拦截):
public dynamic func foobar() -> AnyObject在上面提供的示例中,满足了这些要求.你的类是从传递性派生NSObject通过UIView和UIResponder,但有一些古怪该类别:
尝试将Swizzling代码移动到AppDelegate中,然后完成启动:
//Put this instead in AppDelegate
method_exchangeImplementations(
class_getInstanceMethod(UINavigationBar.self, "sizeThatFits:"),
class_getInstanceMethod(UINavigationBar.self, "sizeThatFits_FixedHeightWhenStatusBarHidden:"))
Run Code Online (Sandbox Code Playgroud)