Search code examples
iosswiftxcodeuinavigationcontrollerstoryboard

Custom Navigation Bar not rectangular


Is it possible to customize the Navigation Bar to look like the picture below? Obviously that has a transparent background not a grey one.

enter image description here

I've tried:

UINavigationBar.appearance().setBackgroundImage(UIImage(named:"pattern.png"),
                                                            for: .default)

That seems to capture the colour but not the format of the navigation bar.

enter image description here

I am trying to avoid creating a custom nav bar with a UIView or anything like that because that would mean a lot of extra work for such a small change.My app has a lot of views using a Navigation Controller.

Thanks in advance.


Solution

  • Use this to add the zigzag pattern to your navigationBar (version2), or you can create a custom UIImage with your desired pattern (version1 (favorite))

    Version 1 using UIImage and using self.navigationController?.navigationBar.setBackgroundImage method

    Getting the image

    import UIKit
    
    extension UIImage {
        
        static func pathZigZagForCGRect(rect: CGRect) ->UIBezierPath
        {
            let width = rect.size.width
            let height = rect.size.height
            
            let zigZagWidth = CGFloat(7)
            let zigZagHeight = CGFloat(5)
            var yInitial = height-zigZagHeight
            
            var zigZagPath = UIBezierPath()
            zigZagPath.move(to: CGPoint(x:0, y:0))
            zigZagPath.addLine(to: CGPoint(x:0, y:yInitial))
            
            var slope = -1
            var x = CGFloat(0)
            var i = 0
            while x < width {
                x = zigZagWidth * CGFloat(i)
                let p = zigZagHeight * CGFloat(slope) - 5
                let y = yInitial + p
                let point = CGPoint(x: x, y: y)
                zigZagPath.addLine(to: point)
                slope = slope*(-1)
                i += 1
            }
            
            zigZagPath.addLine(to: CGPoint(x:width,y: 0))
            zigZagPath.addLine(to: CGPoint(x:0,y: 0))
            zigZagPath.close()
            return zigZagPath
        }
        
    
        static func zigZagImage(rect: CGRect,color:UIColor)->UIImage {
            
            UIGraphicsBeginImageContextWithOptions(rect.size, false, 0)
            var ctx = UIGraphicsGetCurrentContext()!
            ctx.clear(CGRect(x: 0, y: 0, width: rect.size.width, height: rect.size.height))
            
            ctx.setFillColor(color.cgColor)
            let path = UIImage.pathZigZagForCGRect(rect: rect)
            ctx.addPath(path.cgPath)
            ctx.fillPath()
            //draw triangle
            
            let img = UIGraphicsGetImageFromCurrentImageContext()!
            UIGraphicsEndImageContext()
            
            return img
        }
        
    }
    

    Using it

    override func viewDidAppear(_ animated: Bool) {
            super.viewDidAppear(animated)
            self.navigationController?.navigationBar.setBackgroundImage( UIImage.zigZagImage(rect: CGRect(x: 0, y: 0, width: (self.navigationController?.navigationBar.frame.size.width)!, height: (self.navigationController?.navigationBar.frame.height)! + 20),color:UIColor.red).resizableImage(withCapInsets: UIEdgeInsets.zero, resizingMode: .stretch), for: .default)
        }
    

    Result

    enter image description here

    Version 2 using layerMask

    func pathZigZagForView(givenView: UIView) ->UIBezierPath
        {
            let width = givenView.frame.size.width
            let height = givenView.frame.size.height
            
            let zigZagWidth = CGFloat(7)
            let zigZagHeight = CGFloat(5)
            var yInitial = height-zigZagHeight
            
            var zigZagPath = UIBezierPath()
            zigZagPath.move(to: CGPoint(x:0, y:0))
            zigZagPath.addLine(to: CGPoint(x:0, y:yInitial))
            
            var slope = -1
            var x = CGFloat(0)
            var i = 0
            while x < width {
                x = zigZagWidth * CGFloat(i)
                let p = zigZagHeight * CGFloat(slope) - 5
                let y = yInitial + p
                let point = CGPoint(x: x, y: y)
                zigZagPath.addLine(to: point)
                slope = slope*(-1)
                i += 1
            }
            
            zigZagPath.addLine(to: CGPoint(x:width,y: 0))
            zigZagPath.addLine(to: CGPoint(x:0,y: 0))
            zigZagPath.close()
            return zigZagPath
       }
    
    func applyZigZagEffect(givenView: UIView) {
        let shapeLayer = CAShapeLayer(layer: givenView.layer)
        shapeLayer.path = self.pathZigZagForView(givenView: givenView).cgPath
        shapeLayer.frame = givenView.bounds
        shapeLayer.masksToBounds = true
        givenView.layer.mask = shapeLayer
    }
    

    Using it

    override func viewDidAppear(_ animated: Bool) {
            super.viewDidAppear(animated)
            self.navigationController?.navigationBar.backgroundColor = UIColor.red
            self.applyZigZagEffect(givenView: (self.navigationController?.navigationBar)!)
        }
    

    Hope this helps you