Ben Guild
Ben Guild

Reputation: 5156

`UINavigationBar` curved extension?

In Apple's examples for customizing UINavigationBar, they demonstrate the possibility of extending the navigation bar for additional functionality by covering the bottom of the bar and recreating its drop shadow. https://developer.apple.com/library/ios/samplecode/NavBar/Introduction/Intro.html

I'm looking to do this, but with a curved extension. In other words, a semi-circle to make the entire UINavigationBar seem like it were a section of a big circle. See attached:

design mockup

The only way I've thought about possibly doing this so far is to make a very large but contained UIView with a large corner radius to emulate this curve. I did a test, but found that when the size of a UIView exceeds a certain size threshold/ratio, the edges start to distort and it looks as awful as it is inefficient and strange to have a massive but clipped UIView.

Since device sizes vary and screen-orientation changes needs to be animated without the curve distorting, is there any way to possibly produce this sort of curved UINavigationBar?

Upvotes: 1

Views: 1240

Answers (1)

ilovecomputer
ilovecomputer

Reputation: 4738

The only method I came out for now is subclassing UINavigationBar and drawing the shape you want using CAShapeLayer:

import UIKit

class CustomNavigationBar: UINavigationBar {

    var pathLayer: CAShapeLayer?

    init() {
        super.init(frame: CGRectZero)

        setup()
    }

    override init(frame: CGRect) {
        super.init(frame: frame)

        setup()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)

        setup()
    }

    override func drawRect(rect: CGRect) {

        super.drawRect(rect)
    }

    func setup() {
        var bezierPath = UIBezierPath()
        bezierPath.moveToPoint(CGPointMake(0.0, 31.47))
        bezierPath.addCurveToPoint(CGPointMake(186.72, 42.5), controlPoint1: CGPointMake(0.5, 31.47), controlPoint2: CGPointMake(93.22, 42.5))
        bezierPath.addCurveToPoint(CGPointMake(375, 31.47), controlPoint1: CGPointMake(280.22, 42.5), controlPoint2: CGPointMake(375, 31.47))
        bezierPath.addLineToPoint(CGPointMake(375, -0.5))
        bezierPath.addLineToPoint(CGPointMake(0.5, -0.5))
        bezierPath.addLineToPoint(CGPointMake(0.0, 31.47))
        UIColor.blackColor().setStroke()
        bezierPath.lineWidth = 1
        bezierPath.stroke()

        pathLayer = CAShapeLayer()
        pathLayer?.fillColor = UIColor.lightGrayColor().CGColor
        pathLayer?.path = bezierPath.CGPath
        pathLayer?.lineWidth = 1.0
        pathLayer?.strokeColor = UIColor.blackColor().CGColor
        layer.addSublayer(pathLayer!)

        let title = UILabel(frame: CGRect(x: 0, y: 0, width: 60, height: 30))
        title.text = "Title"
        title.textAlignment = .Center
        title.center = self.center
        addSubview(title)

    }
}

Result:

enter image description here

The problem is that the added pathLayer will cover the layers of navigationbar's title and buttons. So if you set the fillColor of pathLayer to color other than clear color, these buttons and titles will be invisible.

To fix the issue, you can add the title and buttons programmatically after the pathLayer is added as I did, or use insertSublayer to insert the pathLayer behind the buttons and title as mentioned in this post CALayer - Place sublayer below storyboard UIButtons?

Upvotes: 1

Related Questions