How to overlap status bar when adding UIView on top of UINavigationBar? - ios

I am using UINavigationController as a rootController with UIViewController. In UIViewController, i am adding custom UIView into the navigationBar(because i want to use CAGradientLayer to my custom UIView).
Everything's working fine except that my custom UIView does not overlap the status bar, which means my custom UIView is padding from top of the navigationBar.
Is there any solution and way to make it overlap statusBar? Here i included UI of what i want to achieve.

Status bar is always there so if you don't want it when you add your view you have to override a property prefersStatusBarHidden of the ViewController where your new View is added like so:-
Declare a new variable say:
var myNewViewIsVisible: Bool = false {
didSet {
DispatchQueue.main.async { [weak self] in
guard let self = self else {return}
self.setNeedsStatusBarAppearanceUpdate()
}
}
}
Then override the property like so: -
override var prefersStatusBarHidden: Bool {
return myNewViewIsVisible
}
Explanation:
First you declare your variable and observe it for changes. If it is changed ie.. when you add your new view, change it to true then force the ViewController to update statatusBar, that will cause it to hide because your myNewViewIsVisible will be true.

let gradient = CAGradientLayer()
gradient.frame = CGRect(x: 0, y: 0, width: navigationBar.frame.width, height: 100)
let leftColor = UIColor.red
let rightColor = UIColor.purple
gradient.colors = [leftColor.cgColor, rightColor.cgColor]
gradient.startPoint = CGPoint(x: 0, y: 0.5)
gradient.endPoint = CGPoint(x: 1.0, y: 0.5)
navigationBar.setBackgroundImage(image(fromLayer: gradient), for: .default)
func image(fromLayer layer: CALayer) -> UIImage {
UIGraphicsBeginImageContext(layer.frame.size)
layer.render(in: UIGraphicsGetCurrentContext()!)
let outputImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return outputImage!
}

You need to hide the navigation bar :
self.navigationController?.setNavigationBarHidden(true, animated: false)
Then you need to show status bar :
override var prefersStatusBarHidden: Bool{
return false
}
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
Also then you have to set the background of the status bar view :
let statusBarView = UIView(frame: CGRect(x: 0, y: 0, width:UIScreen.main.bounds.width, height: UIApplication.shared.statusBarFrame.height))
statusBarView.backgroundColor = UIColor.gray
self.navigationController?.view.addSubview(statusBarView)

Related

Make custom-triggered UIColorPickerViewController hug bottom of screen?

I am using custom control to trigger the presentation of a UIColorPickerViewController. It triggers the color picker, but not as expected.
I was using UIColorWell previously, which does present the picker correctly (but it is an opaque implementation so I don't know how it does so). I don't want to use UIColorWell, because the shape and appearance aren't right for where I'm invoking the picker from.
Everything works exactly as desired except I can't get the color picker to present at the bottom of the screen like UIColorWell would do it. Instead, whatever I've tried to present (or show), UIColorPickerController appears near the top of the screen.
Note: Currently, to present the color picker controller, I'm using the latest iOS 15 trick with sheet controller / detents to try to anchor the picker to the bottom of the screen, but it's not working as expected (it's the technique is from the Apple Docs example, and as I've seen documented online).
What might be happening? What can I do to get the picker at the bottom of the screen, so that it doesn't obstruct my interface?
The Extension:
extension UIView {
func findViewController() -> UIViewController? {
if let nextResponder = self.next as? UIViewController {
return nextResponder
} else if let nextResponder = self.next as? UIView {
return nextResponder.findViewController()
} else {
return nil
}
}
}
Reproducible example (almost. It needs to be implemented/invoked from whatever VC [not shown here]):
This is custom control that that creates a rectangular color picker view (I'm using it to replace UIColorWell). It attempts to locate the VC which contains the view of which this custom control is a subview.
import UIKit
import QuartzCore
class RectangularColorWell : UIControl {
var colorPickerController = UIColorPickerViewController()
var selectedColor : UIColor = UIColor.clear
var lineWidth : CGFloat = 4.0 {
didSet {
self.setNeedsDisplay()
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override init(frame: CGRect) {
super.init(frame: frame)
colorPickerController.supportsAlpha = false
colorPickerController.delegate = self
selectedColor = backgroundColor ?? UIColor.clear
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(CustomToggleControl.controlTapped(_:)))
tapGestureRecognizer.numberOfTapsRequired = 1;
tapGestureRecognizer.numberOfTouchesRequired = 1;
self.addGestureRecognizer(tapGestureRecognizer)
self.setNeedsDisplay()
}
override func draw(_ rect: CGRect) {
guard let ctx = UIGraphicsGetCurrentContext() else { return }
ctx.setLineWidth(lineWidth)
ctx.setFillColor(backgroundColor!.cgColor)
ctx.fill(rect)
drawGradientBorder(rect, context: ctx)
}
func drawGradientBorder(_ rect: CGRect, context: CGContext) {
context.saveGState()
context.setLineWidth(lineWidth)
let path = UIBezierPath(rect: rect)
context.addPath(path.cgPath)
context.replacePathWithStrokedPath()
context.clip()
let rainbowColors : [UIColor] = [ .red, .orange, .yellow, .green, .blue, .purple ]
let colorDistribution : [CGFloat] = [ 0.0, 0.2, 0.4, 0.6, 0.8, 1.0 ]
let gradient = CGGradient(colorsSpace: CGColorSpaceCreateDeviceRGB(), colors: rainbowColors.map { $0.cgColor } as CFArray, locations: colorDistribution)!
context.drawLinearGradient(gradient, start: CGPoint(x: 0, y: 0), end: CGPoint(x: rect.width, y: rect.height), options: [])
context.restoreGState()
}
#objc func controlTapped(_ gestureRecognizer :UIGestureRecognizer) {
UIView.animate(withDuration: 0.2, delay: 0.0, options: UIView.AnimationOptions.curveEaseIn, animations: {
self.alpha = 0.5
}, completion: { (finished: Bool) -> Void in
UIView.animate(withDuration: 0.2, delay: 0.0, options: UIView.AnimationOptions.curveEaseOut, animations: {
self.alpha = 1.0
}, completion: { (finished: Bool) -> Void in
})
})
setNeedsDisplay()
let vc = self.findViewController()
if let sheet = vc?.sheetPresentationController {
sheet.detents = [.medium()]
sheet.largestUndimmedDetentIdentifier = .medium
sheet.prefersScrollingExpandsWhenScrolledToEdge = false
sheet.prefersEdgeAttachedInCompactHeight = true
sheet.widthFollowsPreferredContentSizeWhenEdgeAttached = true
}
vc?.present(colorPickerController, animated: true, completion: nil)
sendActions(for: .touchDown)
}
}
extension RectangularColorWell : UIColorPickerViewControllerDelegate {
func colorPickerViewControllerDidFinish(_ controller : UIColorPickerViewController) {
self.selectedColor = controller.selectedColor
}
func colorPickerViewController(_ controller : UIColorPickerViewController, didSelect color: UIColor, continuously: Bool) {
self.backgroundColor = color
self.setNeedsDisplay()
}
}
#JordanH's final comment caused me to read up on the show/present methods on UIViewController docs. The default presentation style was what I'd been using (e.g. I hadn't set any of the presentation options on the color picker's view controller when I filed the question.
Surprisingly after reading that I could control the style, setting the picker's modalPresentationStyle property to '.pageSheet' which is the one that looks like it would work, to do a partial covering, did not work!.
The solution turned out to be adding colorPickerController.modalPresentationStyle = .popover before calling present()in the custom RectangularColorWellView). It now behaves as if it was presented from a UIColorWell control view.

Managing Custom UITabBar View

Following this repo, I created a custom "tabBar" via a separate uiview that sits behind the native uitabbarcontroller tabBar. The custom tabBar has rounded corners and a shadow.
Everything works great except in the instances I push/pop a new uiviewcontroller onto/from the embedded uinavigationcontroller stack, where I hide the tabBar.
My issue is smoothly toggling the custom tabBar to hide/show during these instances.
I've set .isHidden = true/false for the custom tabBar when a uiviewcontroller is pushed/popped, and either the custom tabBar disappears too early or appears too late relative to the native uitabbarcontroller tabBar.
Any guidance would be appreciated.
class TabBarViewController: UITabBarController {
let customTabBarView: UIView = {
let view = UIView(frame: .zero)
view.backgroundColor = Constants.style.offWhite
view.layer.cornerRadius = 20
view.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
view.clipsToBounds = true
view.layer.masksToBounds = false
view.layer.shadowColor = UIColor.black.cgColor
view.layer.shadowOffset = CGSize(width: 0, height: -8.0)
view.layer.shadowOpacity = 0.12
view.layer.shadowRadius = 10.0
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
self.tabBar.isHidden = true
self.tabBar.backgroundColor = .clear
self.tabBar.barStyle = .default
self.tabBar.isTranslucent = true
addCustomTabBarView()
hideTabBarBorder()
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
customTabBarView.frame = tabBar.frame
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
var newSafeArea = UIEdgeInsets()
newSafeArea.bottom += customTabBarView.bounds.size.height
self.children.forEach({$0.additionalSafeAreaInsets = newSafeArea})
}
//Function invoked when pushing/popping viewControllers onto/from the embedded navigation stack
func toggleCustomTabBarView(shouldHide: Bool) {
self.customTabBarView.isHidden = shouldHide
DispatchQueue.main.async {
UIView.transition(with: self.view, duration: TimeInterval(UINavigationController.hideShowBarDuration), options: .transitionCrossDissolve, animations: {
})
}
}
func addCustomTabBarView() {
customTabBarView.frame = tabBar.frame
view.addSubview(customTabBarView)
view.bringSubviewToFront(self.tabBar)
}
func hideTabBarBorder() {
let tabBar = self.tabBar
tabBar.backgroundImage = UIImage.from(color: .clear)
tabBar.shadowImage = UIImage()
tabBar.clipsToBounds = true
}
}
extension UIImage {
static func from(color: UIColor) -> UIImage {
let rect = CGRect(x: 0, y: 0, width: 1, height: 1)
UIGraphicsBeginImageContext(rect.size)
let context = UIGraphicsGetCurrentContext()
context!.setFillColor(color.cgColor)
context!.fill(rect)
let img = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return img!
}
}
https://stackoverflow.com/a/65285566/9466631
Couldn't find anything as a solve for the dummy view behind the tabBar, but this new solution worked for me that avoids using the dummy view altogether and adds a CAShapeLayer instead.

The nav bar doesn't clip to the top of iphone x

The Nav bar doesnt go to the top of the screen when rounding the edges on iphone x or up. Is there any way to fix this?
IMAGE(WHAT IT LOOKS LIKE NOW)
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.navigationBar.layer.cornerRadius = 20
self.navigationController?.navigationBar.clipsToBounds = true
self.navigationController?.navigationBar.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMaxYCorner]
}
}
A default navigation bar has a background view which will be not generated when you apply custom operations on a layer.
Solution
Create a view with a given color, radius corner and dimension
Convert the view to an image
Set the image view to a navigation bar background property
override func viewDidLoad() {
super.viewDidLoad()
let v = UIView(frame: CGRect(x: 0, y: -UIApplication.shared.statusBarFrame.height, width: view.frame.width, height: UIApplication.shared.statusBarFrame.height + (navigationController?.navigationBar.frame.height ?? 0)))
v.backgroundColor = .red
v.layer.cornerRadius = 20
v.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMaxYCorner]
v.clipsToBounds = true
let i = image(with: v)!
navigationController?.navigationBar.setBackgroundImage(i, for: .default)
}
func image(with view: UIView) -> UIImage? {
UIGraphicsBeginImageContextWithOptions(view.bounds.size, false, 0.0)
defer { UIGraphicsEndImageContext() }
if let context = UIGraphicsGetCurrentContext() {
view.layer.render(in: context)
let image = UIGraphicsGetImageFromCurrentImageContext()
return image
}
return nil
}
You can use xib for add navigation bar for proper navigation bar design. Which is compatible for all devices.
var customNavView: Your_xib_File?
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationViewStup()
}
func navigationViewStup(){
customNavView = Bundle.main.loadNibNamed("NavBar", owner: self, options: nil)?[0] as? NavBarView
customNavView?.frame = (self.navigationController?.navigationBar.bounds)!
customNavView?.tag = 1111
customNavView?.btnVoice.addTarget(self, action: #selector(btnVoicePressed), for: .touchUpInside)
self.navigationController?.navigationBar.addSubview(customNavView!)
}
Or try this,
func position(for bar: UIBarPositioning) -> UIBarPosition {
return .topAttached
}
navigationBar.isTranslucent = false

Globally apply a gradient to the navigation bar and handle orientation changes

I need to apply a gradient globally to my status and navigation bars and have it adjust properly to orientation changes. Because I want this to be global, I'm trying to use UIAppearance. Surprisingly, UIAppearance doesn't make this very easy.
It looks great in Portrait, but the gradient is too tall when in Landscape so you can't see the whole thing:
Here's my code to this point:
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())
}
and I'm using this extension:
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
}
}
I know I could check the orientation and then change the gradient accordingly but I'd need to do that on every view controller so that would defeat the purpose of using UIAppearance and being able to do it in one place.
Most of the SO threads I've found provide solutions for making the top bar's gradient at the view controller level, but not the global level.
EDIT:
Tried answer from #Pan Surakami on my UITabBarController but I still have white navigation bars:
Here's my storybaord setup:
And code:
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)
}
}
One way to implement it is to subclass UINavigationController.
Create a new subclass.
class GradientNavigationController: UINavigationController {}
Override traitCollectionDidChange method.
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())
}
}
Use this subclass instead of UINavigationController. Either change custom subclass on storiboards or use it in code.
EDIT:
Your UITabBarController is configured by the storyboard. So setTabs() used this way has no sense. It just creates another copy of UITabBarController and then removes it. I showed it just as an example of embedding controllers.
Remove the method setTabs().
Open each storyboard which is linked to your tabs.
(I cannot see how they are actually configured they are behind storyboard references.)
Make sure that an initial controller is GradientNavigationController.
enter image description here

How can I hide navigationBar -> Overlay Label but as an image, like Airbnb?

I've experimented a little bit to copy (or at least to have a similar working example) the airbnb navigationBar. For those who doesn't know the app please see the following screenshots:
:
As you may see the navigationBar is at first hidden and then is behind the image but overlays the heading label. I really like the animation how the navigationbar transition smoothly to the front.
I thought to do this with
Hide navigationBar
At the exact position beginn with fading in the navigationBar: Make navigationBar transparent and the increase the alpha
At the end of the animation show the default navigationBar
The implementation looks like: https://github.com/mbecker/AirbnbCopy
import UIKit
private let kTableHeaderHeight: CGFloat = 300.0
private let kTableHeaderCutAway: CGFloat = 60.0
class MainViewController: UIViewController, UIScrollViewDelegate {
#IBOutlet var headerView: UIView!
#IBOutlet weak var scrollView: UIScrollView!
#IBOutlet weak var parkHeading: UILabel!
#IBOutlet weak var parkImage: UIImageView!
var headerMaskLayer: CAShapeLayer!
// create background images for the navigation bar
var navBarImage = UIImage().imageWithColor(UIColor(red:0.11, green:0.64, blue:0.98, alpha:0.0))
var gradientImage32 = UIImage().imageWithColor(UIColor(red:0.11, green:0.64, blue:0.98, alpha:0.0))
let image = UIImage(named: "bg-addo")
let overlay: UIView = UIView(frame: CGRectMake(0, 0, UIScreen.mainScreen().bounds.height, 400))
override func viewDidLoad() {
super.viewDidLoad()
scrollView.delegate = self
// Adjust view
self.automaticallyAdjustsScrollViewInsets = false
// NavigationBar
let attrs = [
NSForegroundColorAttributeName : UIColor.blackColor(),
NSFontAttributeName : UIFont(name: "HelveticaNeue-Bold", size: 17)!
]
self.navigationController!.navigationBar.titleTextAttributes = attrs
self.navigationController!.navigationBar.hidden = true
self.navigationController!.navigationBar.setBackgroundImage(navBarImage, forBarMetrics: .Default)
self.navigationController!.navigationBar.setBackgroundImage(navBarImage, forBarMetrics: .Compact)
// self.navigationController!.navigationBar.shadowImage = UIImage()
self.navigationController!.navigationBar.barStyle = .Default
// Header
overlay.backgroundColor = UIColor(red:0.04, green:0.28, blue:0.44, alpha:0.4)
parkImage.addSubview(overlay)
parkImage.image = image?.imageWithAlpha(1)
headerMaskLayer = CAShapeLayer()
headerMaskLayer.fillColor = UIColor.blackColor().CGColor
headerView.layer.mask = headerMaskLayer
updateHeaderView()
}
func scrollViewDidScroll(scrollView: UIScrollView) {
let heightShowNavBarStart = kTableHeaderHeight - kTableHeaderCutAway - parkHeading.frame.height - 66
let heightShowNavBarEnd = kTableHeaderHeight - kTableHeaderCutAway - 66
print("scrollView.contentOffset.y - \(scrollView.contentOffset.y)")
print("heightShowNavBarStart - \(heightShowNavBarStart)")
print("heightShowNavBarEnd - \(heightShowNavBarEnd)")
let base = parkHeading.frame.height
let counter = heightShowNavBarEnd - scrollView.contentOffset.y
var alpha = counter / base
var navigationBarHidden = false;
if(scrollView.contentOffset.y >= heightShowNavBarStart && scrollView.contentOffset.y <= heightShowNavBarEnd){
} else if (scrollView.contentOffset.y < heightShowNavBarStart ){
navigationBarHidden = true
alpha = 1
} else if(scrollView.contentOffset.y > heightShowNavBarEnd) {
navigationBarHidden = false
alpha = 0
}
print("alpha - \(alpha)")
print("navigationBarHidden - \(navigationBarHidden)")
self.navigationController!.navigationBar.hidden = navigationBarHidden
if(!navigationBarHidden && alpha == 0){
// Show navigationBar && hide headerView parkImage
parkImage.hidden = true
self.navigationController!.navigationBar.setBackgroundImage(nil, forBarMetrics: .Default)
self.navigationController!.navigationBar.setBackgroundImage(nil, forBarMetrics: .Compact)
self.navigationController!.navigationBar.barStyle = .Default
} else {
parkImage.hidden = false
parkImage.image = image?.imageWithAlpha(alpha)
navBarImage = UIImage().imageWithColor(UIColor(red:0.96, green:0.96, blue:0.98, alpha: 1 - alpha))
self.navigationController!.navigationBar.setBackgroundImage(navBarImage, forBarMetrics: .Default)
self.navigationController!.navigationBar.setBackgroundImage(navBarImage, forBarMetrics: .Compact)
overlay.backgroundColor = UIColor(red:0.04, green:0.28, blue:0.44, alpha: alpha * 0.4)
}
}
func updateHeaderView(){
let effectiveHeight = kTableHeaderHeight-kTableHeaderCutAway/2
var headerRect = CGRect(x: 0, y: -effectiveHeight, width: scrollView.bounds.width, height: kTableHeaderHeight)
if scrollView.contentOffset.y < -effectiveHeight {
headerRect.origin.y = scrollView.contentOffset.y
headerRect.size.height = -scrollView.contentOffset.y + kTableHeaderCutAway/2
}
headerView.frame = headerRect
let path = UIBezierPath()
path.moveToPoint(CGPoint(x: 0, y: 0))
path.addLineToPoint(CGPoint(x: headerRect.width, y: 0))
path.addLineToPoint(CGPoint(x: headerRect.width, y: headerRect.height))
path.addLineToPoint(CGPoint(x: 0, y: headerRect.height-kTableHeaderCutAway))
headerMaskLayer?.path = path.CGPath
}
}
extension UIImage {
func imageWithColor(colour: UIColor) -> UIImage {
let rect = CGRectMake(0, 0, 1, 1)
// Create a 1x1 pixel content
UIGraphicsBeginImageContextWithOptions(rect.size, false, 0.0)
colour.setFill()
UIRectFill(rect)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
func imageWithAlpha(alpha: CGFloat) -> UIImage {
UIGraphicsBeginImageContextWithOptions(size, false, scale)
drawAtPoint(CGPointZero, blendMode: .Normal, alpha: alpha)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage
}
}
I think the animation is not very smooth and at some point the header image is not set back to the top of the screen.
Has anyone an idea how to create such an navigationBar?
This is exactly what you want you it allows you to create header bars with flexible heights. Often, this sort of UI paradigm is used to hide "chrome" and make room for more content as a user is scrolling.
https://github.com/bryankeller/BLKFlexibleHeightBar/

Resources