Methode Swizzling in Swift

Es gibt ein kleines Problem mitUINavigationBar das versuche ich zu überwinden. Wenn Sie die Statusleiste mit @ ausblendprefersStatusBarHidden() -Methode in einem Ansichtscontroller (ich möchte die Statusleiste nicht für die gesamte App deaktivieren), verliert die Navigationsleiste 20pt ihrer Höhe, die zur Statusleiste gehört. Grundsätzlich schrumpft die Navigationsleiste.

Ich habe verschiedene Problemumgehungen ausprobiert, aber ich habe festgestellt, dass jede von ihnen Nachteile hat. Dann bin ich auf dieses @ gestoßKategori Fügt mit der Swizzling-Methode eine Eigenschaft mit dem Namen @ hinzfixedHeightWhenStatusBarHidden zumUINavigationBar Klasse, die dieses Problem löst. Ich habe es in Objective-C getestet und es funktioniert. Hier sind dieHeade und derImplementierun des ursprünglichen Objective-C-Codes.

Nun, seit ich meine App in Swift mache, habe ich versucht, sie in Swift zu übersetzen.

Das erste Problem, mit dem ich konfrontiert wurde, war, dass die Swift-Erweiterung keine gespeicherten Eigenschaften haben kann. Also musste ich mich mit der berechneten Eigenschaft begnügen, um das @ zu deklarierefixedHeightWhenStatusBarHidden -Eigenschaft, mit der ich den Wert festlegen kann. Dies löst jedoch ein anderes Problem aus. Anscheinend können Sie berechneten Eigenschaften keine Werte zuweisen. Wie so.

self.navigationController?.navigationBar.fixedHeightWhenStatusBarHidden = true

Ich erhalte den FehlerKann dem Ergebnis dieses Ausdrucks nicht zugewiesen werden.

Wie auch immer unten ist mein Code. Es kompiliert fehlerfrei, funktioniert aber nicht.

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()ie @ -Methode in der Mitte ist auskommentiert, da beim Verlassen der Methode ein Fehler bei der Neudeklaration auftritt.

Ich habe noch nie in Swift oder Objective-C Methoden-Swizzles durchgeführt, daher bin ich mir nicht sicher, in welchem Schritt dieses Problem behoben werden kann oder ob es überhaupt möglich ist.

Kann jemand bitte Licht ins Dunkel bringen?

Vielen Dank

UPDATE 1: Dank newacct wurde mein erstes Problem mit Eigenschaften behoben. Aber der Code funktioniert immer noch nicht. Ich habe festgestellt, dass die Ausführung das @ nicht erreicload() -Methode in der Erweiterung. Aus Kommentaren inDie antworte, ich habe gelernt, dass entweder deine Klasse Nachkomme von @ sein muNSObject, was in meinem Fall,UINavigationBar stammt nicht direkt von @ NSObject aber es implementiertNSObjectProtocol. Ich bin mir nicht sicher, warum das immer noch nicht funktioniert. Die andere Option ist das Hinzufügen von@objc aber Swift erlaubt Ihnen nicht, es zu Erweiterungen hinzuzufügen. Unten ist der aktualisierte Code.

Die Ausgabe ist noch offen.

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: Jasper hat mir geholfen, die gewünschte Funktionalität zu erreichen, aber anscheinend hat es ein paar große Nachteile.

Seit derload()ie @ -Methode in der Erweiterung wurde nicht ausgelöst. Jasper schlug vor, den folgenden Codeblock auf das @ der App-Delegierten zu verschiebedidFinishLaunchingWithOptions Methode

method_exchangeImplementations(class_getInstanceMethod(UINavigationBar.classForCoder(), "sizeThatFits:"), class_getInstanceMethod(UINavigationBar.classForCoder(), "sizeThatFits_FixedHeightWhenStatusBarHidden:"))

Und ich musste den Rückgabewert im Getter von @ fest auf 'true' codierfixedHeightWhenStatusBarHidden -Eigenschaft, da Sie jetzt, da der Swizzling-Code im App-Delegaten ausgeführt wird, keinen Wert von einem View-Controller festlegen können. Dies macht die Erweiterung in Bezug auf die Wiederverwendbarkeit überflüssig.

So ist die Frage noch mehr oder weniger offen. Wenn jemand eine Idee zur Verbesserung hat, antworten Sie bitte.

Antworten auf die Frage(5)

Ihre Antwort auf die Frage