Пользовательское фоновое изображение с большими заголовками NavigationBar в iOS 11

Как установить пользовательское фоновое изображение для большого заголовка NavigationBar в iOS 11? Я использую пользовательский подкласс, который я назначил навигационным контроллерам в раскадровке.

Вот как я создаю свой пользовательский NavBar:

class CustomNavigationController: UINavigationController {

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
        self.navigationBar.tintColor = UIColor(red:1, green:1, blue:1, alpha:0.6)
        self.navigationBar.titleTextAttributes = [NSForegroundColorAttributeName: UIColor.white]
        if #available(iOS 11.0, *) {
            self.navigationBar.prefersLargeTitles = true
            self.navigationItem.largeTitleDisplayMode = .automatic
            self.navigationBar.largeTitleTextAttributes = [NSForegroundColorAttributeName: UIColor.white]
            self.navigationBar.barTintColor = UIColor.green
        }
        self.navigationBar.isTranslucent = false
        self.navigationBar.setBackgroundImage(#imageLiteral(resourceName: "navigationBarBackground"), for: .default)
        self.navigationBar.shadowImage = #imageLiteral(resourceName: "navigationBarShadow")
    }
}

Странно, что setBackgroundImage(image, for: .default) не работает для больших заголовков. Он работал раньше с iOS 10, а также, если я поворачиваю iPhone (и активирую небольшой NavBar), фон возвращается?

Edit: backgroundImage по-прежнему отображается, но как-то скрыт. Только если вы начинаете прокрутку и появляется "нормальная" панель навигации, отображается backgroundImage. В этом случае также игнорируется barTintColor. screenshot GIF

Ответ 1

У меня была такая же проблема, исправлена ​​ее

Удалите setBackgroundImage и используйте цвет barTint с изображением рисунка.

let bgimage = imageWithGradient(startColor: UIColor.red, endColor: UIColor.yellow, size: CGSize(width: UIScreen.main.bounds.size.width, height: 1))
self.navigationBar.barTintColor = UIColor(patternImage: bgimage!)

Получить изображение с цветами градиента

func imageWithGradient(startColor:UIColor, endColor:UIColor, size:CGSize, horizontally:Bool = true) -> UIImage? {

    let gradientLayer = CAGradientLayer()
    gradientLayer.frame = CGRect(x: 0, y: 0, width: size.width, height: size.height)
    gradientLayer.colors = [startColor.cgColor, endColor.cgColor]
    if horizontally {
        gradientLayer.startPoint = CGPoint(x: 0.0, y: 0.5)
        gradientLayer.endPoint = CGPoint(x: 1.0, y: 0.5)
    } else {
        gradientLayer.startPoint = CGPoint(x: 0.5, y: 0.0)
        gradientLayer.endPoint = CGPoint(x: 0.5, y: 1.0)
    }

    UIGraphicsBeginImageContext(gradientLayer.bounds.size)
    gradientLayer.render(in: UIGraphicsGetCurrentContext()!)
    let image = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return image
}

Ответ 2

В iOS 11 вам больше не нужно устанавливать BackgroundImage (Удалить его объявление), если вы используете большие заголовки. Вместо этого вам нужно использовать BarTintColor.

class CustomNavigationController: UINavigationController {

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
        self.navigationBar.tintColor = UIColor(red:1, green:1, blue:1, alpha:0.6)
        self.navigationBar.titleTextAttributes = [NSForegroundColorAttributeName: UIColor.white]
        if #available(iOS 11.0, *) {
            self.navigationBar.prefersLargeTitles = true
            self.navigationItem.largeTitleDisplayMode = .automatic
            self.navigationBar.largeTitleTextAttributes = [NSForegroundColorAttributeName: UIColor.white]
            self.navigationBar.barTintColor = UIColor(red:1, green:1, blue:1, alpha:1)
        }
        else {
            self.navigationBar.setBackgroundImage(#imageLiteral(resourceName: "navigationBarBackground"), for: .default)                
        }
        self.navigationBar.shadowImage = #imageLiteral(resourceName: "navigationBarShadow")
        self.navigationBar.isTranslucent = false
    }
}

Ответ 3

Попробуйте этот код (Swift 4.0):

in viewDidLoad()

self.navigationController?.navigationBar.titleTextAttributes = [NSAttributedStringKey.foregroundColor: UIColor.black]
if #available(iOS 11.0, *) {
    self.navigationController?.navigationBar.prefersLargeTitles = true
    self.navigationItem.largeTitleDisplayMode = .automatic
    self.navigationController?.navigationBar.largeTitleTextAttributes = [NSAttributedStringKey.foregroundColor: UIColor.black]
} else {
    //iOS <11.0
}
self.title = "Title"
self.navigationController?.navigationBar.barTintColor = UIColor(patternImage: #imageLiteral(resourceName: "nav_bg"))
self.navigationController?.navigationBar.isTranslucent = false

Ответ 4

В ответ на oldrinmendez ответ - это решение идеально подходит для горизонтального градиента.

Для ВЕРТИКАЛЬНОГО градиента я смог использовать ту же функцию из ответа oldrinmendez, вызвав ее снова в scrollViewDidScroll. Это постоянно регулирует высоту градиентного изображения по мере прокрутки пользователем.

Начните с функции от oldrinmendez:

func imageWithGradient(startColor:UIColor, endColor:UIColor, size:CGSize, horizontally:Bool) -> UIImage? {

        let gradientLayer = CAGradientLayer()
        gradientLayer.frame = CGRect(x: 0, y: 0, width: size.width, height: size.height)
        gradientLayer.colors = [startColor.cgColor, endColor.cgColor]
        if horizontally {
            gradientLayer.startPoint = CGPoint(x: 0.0, y: 0.5)
            gradientLayer.endPoint = CGPoint(x: 1.0, y: 0.5)
        } else {
            gradientLayer.startPoint = CGPoint(x: 0.5, y: 0)
            gradientLayer.endPoint = CGPoint(x: 0.5, y: 1)
        }

        UIGraphicsBeginImageContext(gradientLayer.bounds.size)
        gradientLayer.render(in: UIGraphicsGetCurrentContext()!)
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return image
    }

Создайте функцию обновления, чтобы вызывать ее с нужными вам опциями:

func updateImageWithGradient() {

        let navBarHeight  = self.navigationController?.navigationBar.frame.size.height
        let statusBarHeight = UIApplication.shared.statusBarFrame.height
        let heightAdjustment: CGFloat = 2

        let gradientHeight = navBarHeight! + statusBarHeight + heightAdjustment

        let bgimage = imageWithGradient(startColor: UIColor.red, endColor: UIColor.orange, size: CGSize(width: UIScreen.main.bounds.size.width, height: gradientHeight), horizontally: false)
        navigationController?.navigationBar.barTintColor = UIColor(patternImage: bgimage!)
    }

Наконец, добавьте функцию обновления в scrollViewDidScroll & ViewDidApper: Используйте ViewDidAppear, чтобы возвращалась правильная высота панели навигации

override func viewDidAppear(_ animated: Bool) {
        updateImageWithGradient()
    }

override func scrollViewDidScroll(_ scrollView: UIScrollView) {
     DispatchQueue.main.async {
        updateImageWithGradient()
       }
    }

Ответ 5

В Xamarin это будет так:

this.NavigationBar.BackgroundColor = UIColor.Clear;

      var gradientLayer = new CAGradientLayer
      {
        Frame = new CGRect(0, 0, UIApplication.SharedApplication.StatusBarFrame.Width,
              UIApplication.SharedApplication.StatusBarFrame.Height + this.NavigationBar.Frame.Height),
        Colors = new CGColor[]
              {Constants.Defaults.Navigation.RealBlueColor.ToCGColor(), Constants.Defaults.Navigation.RealBlueColor.ToCGColor()}
      };

      UIGraphics.BeginImageContext(gradientLayer.Bounds.Size);
      gradientLayer.RenderInContext((UIGraphics.GetCurrentContext()));
      UIImage image = UIGraphics.GetImageFromCurrentImageContext();
      UIGraphics.EndImageContext();

      this.View.Layer.InsertSublayer(gradientLayer, 0);
      this.NavigationBar.BarTintColor = UIColor.FromPatternImage(image);

Этот .View.Layer.Insert не является обязательным. Мне нужно это, когда я "скручиваю" вверх и вниз изображение на навигационной панели

Ответ 6

Вам это нужно;)

navigationController?.view.backgroundColor = .green

Ответ 7

Изменение barTint не работает для меня, поэтому я меняю слой внутри навигационной панели

 navigationBar.layer.backgroundColor = UIColor(patternImage:
        UIImage(named: "BG-Roof1")!.resizableImage(withCapInsets:
            UIEdgeInsets(top: 0, left: 0, bottom: 10, right: 0), resizingMode: .stretch)).cgColor