Open Isuru-Nanayakkara opened 10 years ago
Hi, you can try using it in your project as is. It is possible to use Swift and Objective C together:
Although, it might not work, because I am using "method swizzling" technique. Not sure if it will work in Swift project. However, it is worth trying since it shouldn't be hard.
I was aware of using Objective-C code in Swift via bridging headers and stuff but I wanted to go for a more pure Swift way :)
I did a little bit of research and found that method swizzling is indeed possible in Swift as long as either the class is a descendant of NSObject
or using the @objc
directive. Since UINavigationBar
is indirectly descended from NSObject
, it was possible to swizzling. But this came with a few tradeoffs.
Before explaining those, please have a look at the end result. The Swift equivalent to Objective-C categories is extensions so I implemented this as an extension.
import Foundation
import UIKit
let FixedNavigationBarSize = "FixedNavigationBarSize";
extension UINavigationBar {
var fixedHeightWhenStatusBarHidden: Bool {
get {
// return objc_getAssociatedObject(self, FixedNavigationBarSize).boolValue
return true
}
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)
}
}
/**
This function isn't getting executed.
*/
override public class func load() {
// method_exchangeImplementations(class_getInstanceMethod(self, "sizeThatFits:"), class_getInstanceMethod(self, "sizeThatFits_FixedHeightWhenStatusBarHidden:"))
}
}
load()
method does not fire in Swift. No idea why. So swizzling code inside the method never gets fired either. So I moved it to app delegate's didFinishLaunchingWithOptions
method.method_exchangeImplementations(
class_getInstanceMethod(UINavigationBar.classForCoder(), Selector.convertFromStringLiteral("sizeThatFits:")),
class_getInstanceMethod(UINavigationBar.classForCoder(), Selector.convertFromStringLiteral("sizeThatFits_FixedHeightWhenStatusBarHidden:"))
fixedHeightWhenStatusBarHidden
to true
in a view controller causes the app to crash at the property's getter with the error unexpectedly found nil while unwrapping an Optional value. So I had to hardcode 'true' in the getter in order to get this to work.Now finally the extension works. But the price of all these workarounds is reusability. Since there are hardcoded values and some parts of it spread across the app files, it's not a good, compact solution in Swift.
Unless there is a way to get the swizzling code back into the extension itself.
Anyway I uploaded a test Xcode project here if you wanna take a look at it and give it a try. I got help from StackOverflow to resolve some snags I hit on the way.
Any updates on this?
Hi,
I know it's a bit old, but i just had this problem today, and maybe it can help someone.
It seems that know it can be solved without swizzling or extension. Just create your own subclass of UINavigationBar with the size that fits that you want, then instantiate the NavigationController using the creator that allows to set a custom navbar class.
Looks like this:
class FixedHeightNavbar : UINavigationBar {
open override func sizeThatFits(_ size: CGSize) -> CGSize {
let origSize = super.sizeThatFits(size) // height can be 64 / 44 / 32 ...
let kStatusBarHeight = UIApplication.shared.statusBarFrame.size.height
let interfaceOrientation = UIApplication.shared.statusBarOrientation
if interfaceOrientation == .portrait && kStatusBarHeight == 0 {
//correct and add back the 20 pt
return CGSize(width: origSize.width, height: origSize.height + 20)
}
return origSize
}
}
...
And instantiate nav controller like this:
let nv = UINavigationController(navigationBarClass: FixedHeightNavbar.self, toolbarClass: nil)
nv.setViewControllers([myRootVC], animated: true)
Hi! I'm currently facing this problem and while I was searching for a solution, I came across your category and it seems like just what I've been looking for.
Although the problem is I'm using Swift. Do you think you can convert it in to Swift?