Метод метания в Swift

Есть небольшая проблема сUINavigationBar который я пытаюсь преодолеть Когда вы скрываете строку состояния с помощьюprefersStatusBarHidden() метод в контроллере представления (я не хочу отключать строку состояния для всего приложения), панель навигации теряет 20pt своей высоты, которая принадлежит строке состояния. В основном панель навигации сжимается.

Я пробовал разные обходные пути, но обнаружил, что у каждого из них были недостатки. Потом наткнулся на этокатегория который, используя метод swizzling, добавляет свойство с именемfixedHeightWhenStatusBarHidden кUINavigationBar класс, который решает эту проблему. Я проверил это в Objective-C, и он работает. Вотзаголовок иреализация исходного кода 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 раньше, поэтому я не уверен в следующем шаге, чтобы решить эту проблему, или даже это возможно вообще.

Может кто-нибудь, пожалуйста, пролить свет на это?

Спасибо.

ОБНОВЛЕНИЕ 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:"))
    }

}

ОБНОВЛЕНИЕ 2: Джаспер помог мне достичь желаемой функциональности, но, очевидно, у нее есть несколько основных недостатков.

Посколькуload() метод в расширении не запускался, как предположил Джаспер, я переместил следующий блок кода в делегаты приложения:didFinishLaunchingWithOptions метод.

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

И мне пришлось жестко закодировать возвращаемое значение в значение «истина» в получателеfixedHeightWhenStatusBarHidden свойство, потому что теперь, когда сверкающий код выполняется в делегате приложения, вы не можете установить значение из контроллера представления. Это делает расширение избыточным, когда дело доходит до повторного использования.

Так что вопрос все еще открыт более или менее. Если у кого-то есть идея улучшить его, пожалуйста, ответьте.

Ответы на вопрос(5)

Ваш ответ на вопрос