-4

I would like to add corner radius & shadow on my navigation controller. I know how to do separately, but when I try to put both at the same time, only one is applied. I tried with CALayer, without success.

An Image to illustrated what I want

enter image description here

TheTiger
  • 13,264
  • 3
  • 57
  • 82
Loïc Mazuc
  • 33
  • 1
  • 4
  • Please show your code. Someone may be able to help you. – Au Ris Mar 29 '19 at 12:22
  • I guess your problem is that if you set corner radius, then you have to mask to bounds because you want a rounded shadow; but actually masking to bounds will cut the shadow. – Rico Crescenzio Mar 29 '19 at 12:32
  • I have no code, what you see, that's what I want to do. And yes it's exactly that @Rico Crescenzio – Loïc Mazuc Mar 29 '19 at 12:49
  • @LoïcMazuc We understand your concern but SO is not like that. You will have to show your effort what you made to accomplish this. Your question is like copy paste the answer without any effort. **Rico** is asking for the same. – TheTiger Mar 29 '19 at 12:51
  • Please have a look at few asking rules in [SO help centre](https://stackoverflow.com/help). Checkout the Asking section. – TheTiger Mar 29 '19 at 12:55
  • Also in your question you mentioned `I tried with CALayer` please show what you did. – TheTiger Mar 29 '19 at 12:56

3 Answers3

7

Here is the code,

    // 1. Enable prefersLargeTitles and title
    self.navigationController?.navigationBar.prefersLargeTitles = true
    self.title = "Title"

    // 2. Add left, right bar buttons
    let leftBtn = UIBarButtonItem(title: "Edit", style: .done, target: self, action: #selector(item))
    let rtBtn = UIBarButtonItem(title: "Add", style: .done, target: self, action: #selector(item))

    self.navigationItem.rightBarButtonItem = rtBtn
    self.navigationItem.leftBarButtonItem = leftBtn

    //3. Change default navbar to blank UI
    self.navigationController?.navigationBar.isTranslucent = false
    self.navigationController?.navigationBar.tintColor = UIColor.orange

    self.navigationController?.navigationBar.setBackgroundImage(UIImage(), for: UIBarMetrics.default)
    self.navigationController?.navigationBar.shadowImage = UIImage()
    self.navigationController?.navigationBar.backgroundColor = UIColor.white

    //4. Add shadow and cirner radius to navbar
    let shadowView = UIView(frame: CGRect(x: 0, y: -20,
                                       width: (self.navigationController?.navigationBar.bounds.width)!,
                                       height: (self.navigationController?.navigationBar.bounds.height)! + 20))
    shadowView.backgroundColor = UIColor.white
    self.navigationController?.navigationBar.insertSubview(shadowView, at: 1)

    let shadowLayer = CAShapeLayer()
    shadowLayer.path = UIBezierPath(roundedRect: shadowView.bounds, byRoundingCorners: [.bottomLeft , .bottomRight , .topLeft], cornerRadii: CGSize(width: 20, height: 20)).cgPath

    shadowLayer.fillColor = UIColor.white.cgColor

    shadowLayer.shadowColor = UIColor.darkGray.cgColor
    shadowLayer.shadowPath = shadowLayer.path
    shadowLayer.shadowOffset = CGSize(width: 2.0, height: 2.0)
    shadowLayer.shadowOpacity = 0.8
    shadowLayer.shadowRadius = 2

    shadowView.layer.insertSublayer(shadowLayer, at: 0)

Output:

enter image description here

Edit

You can get height dynamically using

self.navigationController?.view.safeAreaInsets.top

it will return 44 for iPhone X and 20 for iPhone 8

Code

var offset : CGFloat = (self.navigationController?.view.safeAreaInsets.top ?? 20)

let shadowView = UIView(frame: CGRect(x: 0, y: -offset,
                                   width: (self.navigationController?.navigationBar.bounds.width)!,
                                   height: (self.navigationController?.navigationBar.bounds.height)! + offset))

Output iPhoneX

enter image description here

Prashant Tukadiya
  • 15,838
  • 4
  • 62
  • 98
dahiya_boy
  • 9,298
  • 1
  • 30
  • 51
  • Can we use safe area layout . top instead of static value, which might be better what do you think about it ? like `view.safeAreaLayoutGuide` – Prashant Tukadiya Mar 29 '19 at 13:34
  • @PrashantTukadiya Safe area layout does not covers statusbar background, so it looked weird, if I didn't got your point then please try and update my answer for better coding – dahiya_boy Mar 29 '19 at 13:37
  • 1
    I have updated your answer with method I have suggested to you. Please verify. You are free to update your own answer if you don't like my way of format :) Good day – Prashant Tukadiya Mar 29 '19 at 13:56
  • How to remove appended shadowView on viewwilldisappear ? – lauwis Oct 17 '22 at 17:17
2

Best Way is add this extension in your class add extension at the end of your class and Add shadow as well corner radius to any view as well to navigation controller :)

image shows the Xcode view

enter image description here

Output of navigation controller

enter image description here

extension UIView {


@IBInspectable
var cornerRadius: CGFloat {
    get {
        return layer.cornerRadius
    }
    set {
        layer.cornerRadius = newValue
    }
}

@IBInspectable
var borderWidth: CGFloat {
    get {
        return layer.borderWidth
    }
    set {
        layer.borderWidth = newValue
    }
}

@IBInspectable
var borderColor: UIColor? {
    get {
        if let color = layer.borderColor {
            return UIColor(cgColor: color)
        }
        return nil
    }
    set {
        if let color = newValue {
            layer.borderColor = color.cgColor
        } else {
            layer.borderColor = nil
        }
    }
}

@IBInspectable
var shadowRadius: CGFloat {
    get {
        return layer.shadowRadius
    }
    set {
        layer.shadowRadius = newValue
    }
}

@IBInspectable
var shadowOpacity: Float {
    get {
        return layer.shadowOpacity
    }
    set {
        layer.shadowOpacity = newValue
    }
}

@IBInspectable
var shadowOffset: CGSize {
    get {
        return layer.shadowOffset
    }
    set {
        layer.shadowOffset = newValue
    }
}

@IBInspectable
var shadowColor: UIColor? {
    get {
        if let color = layer.shadowColor {
            return UIColor(cgColor: color)
        }
        return nil
    }
    set {
        if let color = newValue {
            layer.shadowColor = color.cgColor
        } else {
            layer.shadowColor = nil
        }
    }
}
}
TheTiger
  • 13,264
  • 3
  • 57
  • 82
Ali Xhah
  • 118
  • 7
0

To solve this issue use custom UIView. See the following code and output as you expected, I hope this helps you.

enter image description here

Using cornerRadius and maskedCorners to you can set specific corner radius.

override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        navigationView.layer.cornerRadius = 20
        navigationView.layer.maskedCorners = [.layerMinXMaxYCorner ,  .layerMaxXMaxYCorner]
        navigationView.addShadow(shadowColor: UIColor.gray.cgColor, shadowOffset: CGSize(width: 1, height: 2), shadowOpacity: 0.5, shadowRadius: 20)
    }

Add Following extension

extension UIView {

    @IBInspectable var shadow: Bool {
        get {
            return layer.shadowOpacity > 0.0
        }
        set {
            if newValue == true {
                self.addShadow()
            }
        }
    }

    @IBInspectable var cornerRadius: CGFloat {
        get {
            return self.layer.cornerRadius
        }
        set {
            self.layer.cornerRadius = newValue

            // Don't touch the masksToBound property if a shadow is needed in addition to the cornerRadius
            if shadow == false {
                self.layer.masksToBounds = true
            }
        }
    }


    func addShadow(shadowColor: CGColor = UIColor.black.cgColor,
                   shadowOffset: CGSize = CGSize(width: 1.0, height: 2.0),
                   shadowOpacity: Float = 0.4,
                   shadowRadius: CGFloat = 3.0) {
        layer.shadowColor = shadowColor
        layer.shadowOffset = shadowOffset
        layer.shadowOpacity = shadowOpacity
        layer.shadowRadius = shadowRadius
    }
}
AtulParmar
  • 4,358
  • 1
  • 24
  • 45