Глобально применить градиент к панели навигации и обрабатывать изменения ориентации - PullRequest
0 голосов
/ 29 января 2019

Мне нужно применить градиент глобально к моему статусу и панелям навигации и настроить его должным образом для изменения ориентации.Поскольку я хочу, чтобы это было глобальным, я пытаюсь использовать UIAppearance.Удивительно, но UIAppearance не делает это очень легким.

В портретной ориентации он выглядит великолепно, но в альбомной ориентации он слишком высокий, поэтому вы не можете видеть все это:

Comparison of portrait and landscape gradient nav bar

Вот мой код на данный момент:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    let navigationBarAppearance = UINavigationBar.appearance()
    navigationBarAppearance.titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.white]
    navigationBarAppearance.isTranslucent = false
    navigationBarAppearance.tintColor = UIColor.white

    let status_height = UIApplication.shared.statusBarFrame.size.height
    let gradientLayer = CAGradientLayer(frame: CGRect(x: 0, y: 0, width: 64, height: status_height + 44), colors: [UIColor.init(hex: "005382"), UIColor.init(hex: "00294B")])
    let layerImage = gradientLayer.createGradientImage()
    navigationBarAppearance.barTintColor = UIColor(patternImage: layerImage ?? UIImage())
}

, и я использую это расширение:

extension CAGradientLayer {
  convenience init(frame: CGRect, colors: [UIColor]) {
    self.init()
    self.frame = frame
    self.colors = []
    for color in colors {
      self.colors?.append(color.cgColor)
    }
    startPoint = CGPoint(x: 0, y: 0)
    endPoint = CGPoint(x: 0, y: 1)
  }

  func createGradientImage() -> UIImage? {

    var image: UIImage? = nil

    UIGraphicsBeginImageContext(bounds.size)

    if let context = UIGraphicsGetCurrentContext() {
      render(in: context)
      image = UIGraphicsGetImageFromCurrentImageContext()
    }

    UIGraphicsEndImageContext()

    return image
  } 
}

Я знаюЯ мог бы проверить ориентацию и затем соответствующим образом изменить градиент, но мне нужно было бы сделать это на каждом контроллере вида, чтобы не использовать цель UIAppearance и возможность сделать это в одном месте.

Большинство обнаруженных мной потоков SO предоставляют решения для создания градиента верхней панели на уровне контроллера представления, но не на глобальном уровне.

EDIT : пробный ответ от @Pan Surakami onмой UITabBarController, но у меня все еще есть белые навигационные панели:

enter image description here

Вот моя настройка storybaord:

enter image description here

и код:

class MenuTabBarController: UITabBarController {
    var notificationsVM = NotificationsVModel()
    var hasNewAlerts: Bool = false

    override func viewDidLoad() {
      super.viewDidLoad()

      setTabs()

      styleUI()

      notificationsVM.fetchData { (success, newNotifications) in
            if success {
                self.hasNewAlerts = newNotifications.count > 0 ? true : false
                DispatchQueue.main.async {
                    if let tabBarItems = self.tabBar.items {
                        for (_, each) in tabBarItems.enumerated() {
                            if each.tag == 999 { //only update the alerts tabBarItem tag == '999'
                                self.updateAlertBadgeIcon(self.hasNewAlerts, each)
                            }
                        }
                    }
                }
            }
        }
    }

  fileprivate func setTabs() {
    let tab1 = GradientNavigationController(rootViewController: FeedViewController())
    let tab2 = GradientNavigationController(rootViewController: NotificationsTableViewController())
    let tab3 = GradientNavigationController(rootViewController: SearchViewController())
    let tab4 = GradientNavigationController(rootViewController: ComposeDiscussionViewController())
    UITabBarController().setViewControllers([tab1, tab2, tab3, tab4], animated: false)
  }

    func updateAlertBadgeIcon(_ hasAlerts: Bool, _ item: UITabBarItem) {
        if hasAlerts {
            item.image = UIImage(named: "alert-unselected-hasAlerts")?.withRenderingMode(UIImage.RenderingMode.alwaysOriginal)
            item.selectedImage = UIImage(named: "alert-selected-hasAlerts")?.withRenderingMode(UIImage.RenderingMode.alwaysOriginal)
        } else {
            hasNewAlerts = false
            item.image = UIImage(named: "alert-unselected-noAlerts")?.withRenderingMode(UIImage.RenderingMode.alwaysOriginal)
            item.selectedImage = UIImage(named: "alert-selected-noAlerts")?.withRenderingMode(UIImage.RenderingMode.alwaysOriginal)
        }
    }

    // UITabBarDelegate
    override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
        if item.tag == 999 { //alerts tabBarItem tag == '999'
            updateAlertBadgeIcon(hasNewAlerts, item)
        }
      if item.tag == 0 { //Feed Item clicked
        if let feedNav = children[0] as? UINavigationController, let feedVC = feedNav.viewControllers[0] as? FeedViewController {
          feedVC.tableView.reloadData()
        }

      }
    }

    func styleUI() {
        UITabBar.appearance().backgroundImage = UIImage.colorForNavBar(color:.lightGrey4)
        UITabBar.appearance().shadowImage = UIImage.colorForNavBar(color:.clear) 
        tabBar.layer.shadowOffset = CGSize.zero
        tabBar.layer.shadowRadius = 2.0
        tabBar.layer.shadowColor = UIColor.black.cgColor
        tabBar.layer.shadowOpacity = 0.30
        UITabBarItem.appearance().setTitleTextAttributes([NSAttributedString.Key.foregroundColor : UIColor.grey2,
                                                          NSAttributedString.Key.font: UIFont(name: "AvenirNext-DemiBold", size: 12) as Any],
                                                         for: .normal)
        UITabBarItem.appearance().setTitleTextAttributes([NSAttributedString.Key.foregroundColor : UIColor.darkSapphire,
                                                          NSAttributedString.Key.font: UIFont(name: "AvenirNext-DemiBold", size: 12) as Any],
                                                         for: .selected)
    }
}

1 Ответ

0 голосов
/ 30 января 2019

Один из способов реализовать это - создать подкласс UINavigationController.

  1. Создать новый подкласс.
class GradientNavigationController: UINavigationController {}
Переопределить traitCollectionDidChange метод.
class GradientNavigationController: UINavigationController {
    override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
        super.traitCollectionDidChange(previousTraitCollection)

        let status_height = UIApplication.shared.statusBarFrame.size.height
        let gradientLayer = CAGradientLayer(frame: CGRect(x: 0, y: 0, width: 64, height: status_height + 44), colors: [[UIColor.init(hex: "005382"), UIColor.init(hex: "00294B")])
        let layerImage = gradientLayer.createGradientImage()
        self.navigationBar.barTintColor = UIColor(patternImage: layerImage ?? UIImage())
    }
}
Используйте этот подкласс вместо UINavigationController.Либо измените пользовательский подкласс на сторибордах, либо используйте его в коде.

РЕДАКТИРОВАТЬ : ваш UITabBarController настраивается раскадровкой.Так что setTabs() Использовать этот способ бессмысленно.Он просто создает еще одну копию UITabBarController, а затем удаляет ее.Я показал это как пример встраивания контроллеров.

  1. Удалить метод setTabs().
  2. Откройте каждую раскадровку, которая связана с вашими вкладками.(Я не вижу, как они на самом деле настроены, они находятся за ссылками раскадровки.)
  3. Убедитесь, что начальный контроллер GradientNavigationController.

введите описание изображения здесь

...