简体   繁体   中英

Animate UIView on a navigationBar in Swift

I´m trying to make an status banner animate on top of a navigationBar. The animation works fine if I add the banner on the viewControllers view, but will end up behind the navigation bar. If I add the banner to the navigation bar, the banner "pop" on the navigationBar, but will not preform animation. Same problem if I add the banner view to the keyWindow. I also tried to manipulate the banner view and the navigationBars layers zPosition without any luck. Anyone hav an idea?

Here is my code...

import UIKit

class BellaBannerView : UIView {
var view : UIView!
var style : BannerStyle!
var position : BannerTopAnchor!

var bannerView : UIView!
var messageLabel : UILabel!
var dissmissButton : UIButton!
var offsetConstraintConstant : CGFloat!
var testconst : NSLayoutConstraint!

override init(frame: CGRect) {
    super.init(frame: frame)
}
convenience init(view: ViewController, style : BannerStyle, pos : BannerTopAnchor) {
self.init(frame: CGRect.zero)
    self.view = view.view
    self.style = style
    self.position = pos
    self.offsetConstraintConstant = style.bannerSize()
    self.translatesAutoresizingMaskIntoConstraints = false
    self.clipsToBounds = true

    if let isNavigation = view.navigationController?.view {
        isNavigation.addSubview(self)
    } else {
        self.view.addSubview(self)
    }

    initViews(style: style, pos: pos)
    addSubview(bannerView)
    bannerView.addSubview(dissmissButton)
    bannerView.addSubview(messageLabel)

    setConstraints(view: view, pos: pos)
}

required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}

func initViews(style : BannerStyle, pos : BannerTopAnchor) {

    bannerView = {
        let view = UIView()
        view.backgroundColor = style.backgroundColor()
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()

    dissmissButton = {
        let btn = UIButton()
        btn.translatesAutoresizingMaskIntoConstraints = false
        btn.backgroundColor = style.backgroundColor()
        btn.setImage(UIImage(named: "dissmiss"), for: .normal)
        btn.addTarget(self, action: #selector(hide), for: .allTouchEvents)
        return btn
    }()

    messageLabel = {
        let label = UILabel()
        label.text = "BESKJED beskjed Beskjed"
        label.textColor = .lightGray
        label.textAlignment = .center
        label.translatesAutoresizingMaskIntoConstraints = false
        return label
    }()

}

func show() {
    UIView.animate(withDuration: 0.5, animations: { () -> Void in
        self.testconst.constant = 0
        self.view.layoutIfNeeded()
    })
}

func hide() {
    UIView.animate(withDuration: 0.5, animations: { () -> Void in
        self.testconst.constant = -self.offsetConstraintConstant
        self.view.layoutIfNeeded()
    })
}

func setConstraints(view : ViewController, pos : BannerTopAnchor) {
    self.heightAnchor.constraint(equalToConstant: style.bannerSize()).isActive = true
    self.widthAnchor.constraint(equalToConstant: UIScreen.main.bounds.width).isActive = true
    self.topAnchor.constraint(equalTo: self.view.topAnchor, constant: pos.topPosition()).isActive = true
    self.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true

    bannerView.heightAnchor.constraint(equalTo: self.heightAnchor).isActive = true
    bannerView.widthAnchor.constraint(equalTo: self.widthAnchor).isActive = true
    bannerView.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true
    self.testconst = bannerView.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -self.offsetConstraintConstant)
    self.testconst.isActive = true

    dissmissButton.bottomAnchor.constraint(equalTo: bannerView.bottomAnchor).isActive = true
    dissmissButton.trailingAnchor.constraint(equalTo: bannerView.trailingAnchor).isActive = true
    dissmissButton.widthAnchor.constraint(equalToConstant: style.buttonSize()).isActive = true
    dissmissButton.heightAnchor.constraint(equalToConstant: style.buttonSize()).isActive = true

    messageLabel.leadingAnchor.constraint(equalTo: bannerView.leadingAnchor, constant: style.buttonSize()).isActive = true
    messageLabel.trailingAnchor.constraint(equalTo: bannerView.trailingAnchor, constant: -style.buttonSize()).isActive = true
    messageLabel.bottomAnchor.constraint(equalTo: bannerView.bottomAnchor).isActive = true
    messageLabel.heightAnchor.constraint(equalToConstant: style.bannerSize()).isActive = true
}

enum BannerTopAnchor {
    case top
    case statusBar
    case navbar

    func topPosition() -> CGFloat {
        switch self {
        case .top:
            return 0
        case .statusBar:
            return UIApplication.shared.statusBarFrame.height
        case .navbar:
            return 64
        }
    }
}


enum BannerStyle {
    case success
    case info
    case offline
    case online

    func backgroundColor() -> UIColor {
        switch self {
        case .success, .online:
            return UIColor.green//.bellaSuccessBannerBackground()
        case .info:
            return UIColor.blue//.bellaDarkishBlueColor()
        case .offline:
            return UIColor.red//.bellaLipstickColor()
        }
    }

    func bannerSize() -> CGFloat {
        switch self {
        case .info:
            return 50
        case .online, .offline, .success:
            return 25
        }
    }

    func buttonSize() -> CGFloat {
        switch self {
        case .info:
            return 50
        default:
           return 0
        }
    }

    func textColor() -> UIColor {
        switch self {
        case .success, .online:
            return UIColor.green//bellaDarkishGreenColor()
        case .info:
            return UIColor.brown//bellaBeigeColor()
        case .offline:
            return UIColor.white
        }
    }

    func dismissable() -> Bool {
        switch self {
        case .success, .offline, .online:
            return false
        case .info:
            return true
        }
    }
}
}

Problem fixed. Updated layout before starting the animation

    func show() {
    self.layoutIfNeeded()
    self.testconst.constant = 0
    UIView.animate(withDuration: 0.5, animations: { () -> Void in
        self.layoutIfNeeded()
    })
}

func hide() {
    self.layoutIfNeeded()
    self.testconst.constant = -self.offsetConstraintConstant
    UIView.animate(withDuration: 0.5, animations: { () -> Void in
        self.layoutIfNeeded()
    })
}

So the problem here is that the navigation controller take your view controller's view and adds it to it's content view, which is behind the navigation bar. You need to add your banner to something that is above the navigation bar.

You have a few options here, you can add it to the navigation controllers view directly:

self.navigationController?.view.addSubview(bannerView)

this should work fine, though I am always hesitant to play with a UINavigationController view hierarchy manually, as I always see it as a manager hierarchy.

In my opinion a better approach would be to add it straight to the window, which will have the added benefit of making sure it is on top of everything:

self.view.window?.addSubview(bannerView)

this accesses the root window that self 's view is in, and adds your banner to that, on top of everything.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM