web-dev-qa-db-fra.com

Impossible de changer la couleur de l'invite UINavigationBar

Je ne peux pas changer la couleur de l'invite dans la barre de navigation. J'ai essayé le code ci-dessous dans viewDidLoad, mais rien ne se passe.

self.navigationController?.navigationBar.titleTextAttributes = [NSAttributedStringKey.foregroundColor: UIColor.white]

 navigation bar image

Est-ce que je manque quelque chose? Le code ci-dessus est-il faux?

13
Jake

Il semble que vous ayez raison à propos de celui-ci. Vous devez utiliser UIAppearance pour styler le texte d'invite sur iOS 11.

J'ai déposé le radar n ° 34758558 indiquant que la propriété titleTextAttributes cessait de fonctionner pour Prompt dans iOS 11.

La bonne nouvelle est qu'il existe plusieurs solutions que nous pouvons découvrir en utilisant le débogueur de hiérarchie de vues de Xcode:

 Screenshot of UINavigation Bar Prompt on iOS 11

// 1. This works, but potentially changes *all* labels in the navigation bar. 
// If you want this, it works.
UILabel.appearance(whenContainedInInstancesOf: [UINavigationBar.self]).textColor = UIColor.white

L'invite n'est qu'un UILabel. Si nous utilisons le whenContainedInInstancesOf: de UIAppearance, nous pouvons assez facilement mettre à jour la couleur comme nous le souhaitons.

Si vous regardez de plus près, vous remarquerez qu'il existe également une vue wrapper sur UILabel. Il a sa propre classe qui pourrait répondre à UIAppearance ...

 Screenshot of private UINavigationBar subview

// 2. This is a more precise workaround but it requires using a private class.

if let promptClass = NSClassFromString("_UINavigationBarModernPromptView") as? UIAppearanceContainer.Type
{
  UILabel.appearance(whenContainedInInstancesOf: [promptClass]).textColor = UIColor.white
}

Je vous conseillerais de vous en tenir à la solution plus générale, car elle n'utilise pas d'API privée. (Examen de l'application, etc.) Découvrez ce que vous obtenez avec l'une de ces deux solutions:

 Properly colored Prompt text

11
Moshe

J'ai réussi à faire en sorte que la couleur blanche de l'invite sur iOS 11 règle le barStyle en noir. J'ai défini les autres attributs de couleur (comme la couleur d'arrière-plan souhaitée) à l'aide du proxy d'apparence: 

myNavbar.barStyle = UIBarStyleBlack; // Objective-C
myNavbar.barStyle = .black // Swift
12
Markus Forsström

Vous pouvez utiliser

for view in self.navigationController?.navigationBar.subviews ?? [] {
    let subviews = view.subviews
    if subviews.count > 0, let label = subviews[0] as? UILabel {
        label.textColor = UIColor.white
        label.backgroundColor = UIColor.red
    }
}

Ce sera une solution temporaire jusqu'à ce qu'ils résolvent le problème.

2
Igor Lebedev

Version plus compliquée pour prendre en charge les anciens et les nouveaux iOS

    func updatePromptUI(for state: State) {
    if (state != .Online) {
        //workaround for SOFT-7019 (iOS 11 bug - Offline message has transparent background)
        if #available(iOS 11.0, *) {
            showPromptView()
        } else {
            showOldPromptView()
        }
    }
    else {
        self.navigationItem.Prompt = nil
        if #available(iOS 11.0, *) {
            self.removePromptView()
        } else {
            self.navigationController?.navigationBar.titleTextAttributes = nil
            self.navigationController?.navigationBar.titleTextAttributes = [NSAttributedStringKey.foregroundColor:UIColor.lightGray]
        }
    }
}

private func showOldPromptView() {
    self.navigationItem.Prompt = "Network Offline. Some actions may be unavailable."
    let navbarFont = UIFont.systemFont(ofSize: 16)
    self.navigationController?.navigationBar.titleTextAttributes = [NSAttributedStringKey.font: navbarFont, NSAttributedStringKey.foregroundColor:UIColor.white]
}

private func showPromptView() {
    self.navigationItem.Prompt = String()
    self.removePromptView()

    let promptView = UIView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 18))
    promptView.backgroundColor = .red
    let promptLabel = UILabel(frame: CGRect(x: 0, y: 2, width: promptView.frame.width, height: 14))
    promptLabel.text = "Network Offline. Some actions may be unavailable."
    promptLabel.textColor = .white
    promptLabel.textAlignment = .center
    promptLabel.font = promptLabel.font.withSize(13)
    promptView.addSubview(promptLabel)
    self.navigationController?.navigationBar.addSubview(promptView)
}

private func removePromptView() {
    for view in self.navigationController?.navigationBar.subviews ?? [] {
        view.removeFromSuperview()
    }
}
1
Igor Lebedev

Je suggère d'utiliser une sous-classe UINavigationBar personnalisée et de remplacer layoutSubviews:

- (void)layoutSubviews {
    [super layoutSubviews];

    if (self.topItem.Prompt) {
        UILabel *promptLabel = [[self recursiveSubviewsOfKind:UILabel.class] selectFirstObjectUsingBlock:^BOOL(UILabel *label) {
            return [label.text isEqualToString:self.topItem.Prompt];
        }];
        promptLabel.textColor = self.tintColor;
    }
}

En gros, je énumère toutes les étiquettes UIL de la hiérarchie de sous-vues et vérifie si leur texte correspond au texte de l’invite. Ensuite, nous définissons le textColor sur la teinteColor (n'hésitez pas à utiliser une couleur personnalisée). De cette façon, nous n'avons pas à coder en dur la classe privée _UINavigationBarModernPromptView en tant que superview du label Prompt. Le code est donc un peu plus évolutif. 

La conversion du code en Swift et l’implémentation des méthodes auxiliaires recursiveSubviewsOfKind: et selectFirstObjectUsingBlock: restent un exercice pour le lecteur ????.

0
Ortwin Gentz

Essayez ceci: ->

navController.navigationBar.titleTextAttributes = [NSAttributedStringKey.foregroundColor.rawValue: UIColor.red]
0
arpita

J'ai trouvé la solution suivante pour iOS 11. Vous devez définir le nom viewDidLoadnavigationItem.Prompt = UINavigationController.fakeUniqueText

    navigationController?.promptLabel(completion: { label in
    label?.textColor = .white
    label?.font = Font.regularFont(size: .p12)
        })

    extension UINavigationController {
    public static let fakeUniqueText = "\n\n\n\n\n"
    func promptLabel(completion: @escaping (UILabel?) -> Void) {
        gloabalThread(after: 0.5) { [weak self] in
            guard let `self` = self else {
                return
            }
            let label = self.findPromptLabel(at: self.navigationBar)
            mainThread {
                completion(label)
            }
        }
    }

    func findPromptLabel(at view: UIView) -> UILabel? {
        if let label = view as? UILabel {
            if label.text == UINavigationController.fakeUniqueText {
                return label
            }
        }
        var label: UILabel?
        view.subviews.forEach { subview in
            if let promptLabel = findPromptLabel(at: subview) {
                label = promptLabel
            }
        }
        return label
    }
    }


    public func mainThread(_ completion: @escaping SimpleCompletion) {
        DispatchQueue.main.async(execute: completion)
    }


    public func gloabalThread(after: Double, completion: @escaping SimpleCompletion) {
       DispatchQueue.global().asyncAfter(deadline: .now() + after) {
       completion()
    }
}

0
Max Tymchii

La première réponse de Moshe n'a pas fonctionné pour moi car cela a changé les étiquettes à l'intérieur des VCs système, tels que les VCs de composition de courrier et de texte. Je pourrais changer le fond de ces barres de navigation, mais cela ouvre toute une boîte de Pandore. Je ne voulais pas suivre la route de la classe privée, donc je n'ai changé que les étiquettes UIL contenues dans ma sous-classe de barre de navigation personnalisée.

UILabel.appearance(whenContainedInInstancesOf: [NavigationBar.self]).textColor = UIColor.white
0
Steve Moser