Невозможно изменить цвет подсказки UINavigationBar

Я не могу изменить цвет приглашения на панели навигации. Я пробовал код ниже в viewDidLoad, но ничего не происходит.

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

изображение на навигационной панели

Я что-то упустил? Является ли код неправильным?

Ответ 1

Кажется, вы правы в этом. Для стилизации текста приглашения на iOS 11 вам нужно использовать UIAppearance.

Я подал радар # 34758558, что свойство titleTextAttributes просто перестало работать для приглашения в iOS 11.

Хорошей новостью является то, что есть несколько способов обхода, которые мы можем выявить с помощью отладчика иерархии представлений Xcode:

Снимок экрана с подсказкой строки UINavigation на 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

Приглашение - это всего лишь UILabel. Если мы используем UIAppearance whenContainedInInstancesOf:, мы можем довольно легко обновить цвет так, как хотим.

Если вы внимательно присмотритесь, вы заметите, что в UILabel также есть оболочка. У этого есть свой класс, который мог бы ответить на UIAppearance...

Снимок экрана с закрытым подзапросом UINavigationBar

// 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
}

Я бы посоветовал придерживаться более общего решения, поскольку он не использует частный API. (Обзор приложений и т.д.) Проверьте, что вы получаете с любым из этих двух решений:

Правильно окрашенный текст подсказки

Ответ 2

Мне удалось сделать приглашение цвета white на iOS 11, чтобы barStyle был черным. Я установил другие цветовые атрибуты (например, желаемый цвет фона) с помощью прокси-сервера вида:

myNavbar.barStyle = UIBarStyleBlack; // Objective-C
myNavbar.barStyle = .black // Swift

Ответ 3

Вы можете использовать

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
    }
}

Это будет временный обходной путь, пока они не исправят это

Ответ 4

Более сложная версия для поддержки старой и новой 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()
    }
}

Ответ 5

Попробуйте это: →

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

Ответ 6

Первый ответ моше не сработал для меня, потому что он изменил метки внутри системных ВК, такие как почта и текст, составляют ВК. Я мог бы изменить фон этих навигационных панелей, но это открывает еще одну банку с червями. Я не хотел идти по пути частного класса, поэтому я изменил только UILabels, содержащиеся в моем подклассе пользовательской панели навигации.

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

Ответ 7

Я нашел следующую работу вокруг iOS 11. Вам нужно установить на viewDidLoad navigationItem.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()
    }
}

Ответ 8

Я предлагаю использовать пользовательский подкласс UINavigationBar и переопределить 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;
    }
}

По сути, я перечисляю все метки UILabel в иерархии подпредставлений и проверяю, соответствует ли их текст подсказке. Затем мы устанавливаем textColor в tintColor (не стесняйтесь использовать пользовательский цвет). Таким образом, нам не нужно жестко кодировать закрытый класс _UINavigationBarModernPromptView в качестве суперпредставления метки приглашения. Таким образом, код будет более перспективным.

Преобразование кода в Swift и реализация вспомогательных методов recursiveSubviewsOfKind: и selectFirstObjectUsingBlock: оставлены читателю в качестве упражнения 😉.

Ответ 9

Вы можете попробовать это:

import UIKit
class ViewController: UITableViewController {
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        updatePrompt()
    }
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        updatePrompt()
    }
    func updatePrompt() {
        navigationItem.prompt = " "
        for view in navigationController?.navigationBar.subviews ?? [] where NSStringFromClass(view.classForCoder) == "_UINavigationBarModernPromptView" {
            if let prompt = view.subviews.first as? UILabel {
                prompt.text = "Hello Red Prompt"
                prompt.textColor = .red
            }
        }
        navigationItem.title = "This is the title (Another color)"
    }
}

Screenshot