I have a bunch of irregularly-shaped keyboards that I'd like to put separate borders around. Each key is a separate UIView and they're all contained in a wrapper Keyboard view. They look like this:
Ideally the borders should all look like the red one in the bottom left (mocked up in Preview) and have rounded corners when necessary, but I'd consider just getting the right shape a good start.
What's the best way to do this? Shapes and masks? A complex path? It seems like I'd need CALayers either way but I'm sort of new at those.
Any suggestions are greatly appreciated. Thanks so much!
Jake
I figured it out! I ended up using CAShapeLayers
and UIBezierPaths
to draw a border around the keyboards; I used arcs to simulate rounded corners:
func createBezier(key1Num: Int, key2Num: Int, key3Num: Int, key4Num: Int) {
borderPath = UIBezierPath()
let key1 = self.keys[key1Num].frame
let key2 = self.keys[key2Num].frame
let key3 = self.keys[key3Num].frame
let key4 = self.keys[key4Num].frame
let arcRadius = key1.height * 1/32
let start = CGPoint(x: key1.origin.x, y: key1.origin.y)
func bothEdgeNotesBlack() {
borderPath.move(to: start)
borderPath.addLine(to: CGPoint(x: start.x, y: key1.height * 31/32))
borderPath.addArc(withCenter: CGPoint(x: start.x + arcRadius, y: key1.height * 31/32), radius: arcRadius, startAngle: leftAng, endAngle: bottomAng, clockwise: false)
borderPath.addLine(to: CGPoint(x: key2.origin.x, y: key1.height))
borderPath.addLine(to: CGPoint(x: key2.origin.x, y: key2.height - arcRadius))
borderPath.addArc(withCenter: CGPoint(x: key2.origin.x + arcRadius, y: key2.height - arcRadius), radius: arcRadius, startAngle: leftAng, endAngle: bottomAng, clockwise: false)
borderPath.addLine(to: CGPoint(x: key3.origin.x + key3.width - arcRadius, y: key2.height))
borderPath.addArc(withCenter: CGPoint(x: key3.origin.x + key3.width - arcRadius, y: key2.height - arcRadius), radius: arcRadius, startAngle: bottomAng, endAngle: rightAng, clockwise: false)
borderPath.addLine(to: CGPoint(x: key3.origin.x + key3.width, y: key4.height))
borderPath.addLine(to: CGPoint(x: key4.origin.x + key4.width - arcRadius, y: key4.height))
borderPath.addArc(withCenter: CGPoint(x: key4.origin.x + key4.width - arcRadius, y: key4.height - arcRadius), radius: arcRadius, startAngle: bottomAng, endAngle: rightAng, clockwise: false)
borderPath.addLine(to: CGPoint(x: key4.origin.x + key4.width, y: key4.origin.y))
borderPath.close()
}
func leftBlackrightWhite() {
borderPath.move(to: start)
borderPath.addLine(to: CGPoint(x: start.x, y: key1.height * 31/32))
borderPath.addArc(withCenter: CGPoint(x: start.x + arcRadius, y: key1.height * 31/32), radius: arcRadius, startAngle: leftAng, endAngle: bottomAng, clockwise: false)
borderPath.addLine(to: CGPoint(x: key2.origin.x, y: key1.height))
borderPath.addLine(to: CGPoint(x: key2.origin.x, y: key2.height - arcRadius))
borderPath.addArc(withCenter: CGPoint(x: key2.origin.x + arcRadius, y: key2.height - arcRadius), radius: arcRadius, startAngle: leftAng, endAngle: bottomAng, clockwise: false)
borderPath.addLine(to: CGPoint(x: key4.origin.x + key4.width - arcRadius, y: key4.height))
borderPath.addArc(withCenter: CGPoint(x: key4.origin.x + key4.width - arcRadius, y: key4.height - arcRadius), radius: arcRadius, startAngle: bottomAng, endAngle: rightAng, clockwise: false)
borderPath.addLine(to: CGPoint(x: key4.origin.x + key4.width, y: key4.origin.y))
borderPath.close()
}
func leftWhiteRightBlack() {
borderPath.move(to: start)
borderPath.addLine(to: CGPoint(x: start.x, y: key1.height * 31/32))
borderPath.addArc(withCenter: CGPoint(x: start.x + arcRadius, y: key1.height * 31/32), radius: arcRadius, startAngle: leftAng, endAngle: bottomAng, clockwise: false)
borderPath.addLine(to: CGPoint(x: key3.origin.x + key3.width - arcRadius, y: key3.height))
borderPath.addArc(withCenter: CGPoint(x: key3.origin.x + key3.width - arcRadius, y: key3.height - arcRadius), radius: arcRadius, startAngle: bottomAng, endAngle: rightAng, clockwise: false)
borderPath.addLine(to: CGPoint(x: key3.origin.x + key3.width, y: key4.height))
borderPath.addLine(to: CGPoint(x: key4.origin.x + key4.width - arcRadius, y: key4.height))
borderPath.addArc(withCenter: CGPoint(x: key4.origin.x + key4.width - arcRadius, y: key4.height - arcRadius), radius: arcRadius, startAngle: bottomAng, endAngle: rightAng, clockwise: false)
borderPath.addLine(to: CGPoint(x: key4.origin.x + key4.width, y: key4.origin.y))
borderPath.close()
}
func bothEdgeNotesWhite() {
borderPath.move(to: start)
borderPath.addLine(to: CGPoint(x: start.x, y: key1.height * 31/32))
borderPath.addArc(withCenter: CGPoint(x: start.x + arcRadius, y: key1.height * 31/32), radius: arcRadius, startAngle: leftAng, endAngle: bottomAng, clockwise: false)
borderPath.addLine(to: CGPoint(x: key4.origin.x + key4.width - arcRadius, y: key4.height))
borderPath.addArc(withCenter: CGPoint(x: key4.origin.x + key4.width - arcRadius, y: key4.height - arcRadius), radius: arcRadius, startAngle: bottomAng, endAngle: rightAng, clockwise: false)
borderPath.addLine(to: CGPoint(x: key4.origin.x + key4.width, y: key4.origin.y))
borderPath.close()
}
switch self.keys[key1Num].keyType {
case 2, 5, 7, 10, 12: // 1st key is black
switch self.keys[key4Num].keyType {
case 2, 5, 7, 10, 12: // last key is black
bothEdgeNotesBlack()
case 1, 3, 4, 6, 8, 9, 11:
leftBlackrightWhite() // last key is white
default:
()
}
case 1, 3, 4, 6, 8, 9, 11: // 1st key is white
switch self.keys[key4Num].keyType {
case 2, 5, 7, 10, 12: // last key is black
leftWhiteRightBlack()
case 1, 3, 4, 6, 8, 9, 11: // last key is white
bothEdgeNotesWhite()
default:
()
}
default:
()
}
}
func borderBezier(key1Num: Int, key2Num: Int, key3Num: Int, key4Num: Int) {
self.createBezier(key1Num: key1Num, key2Num: key2Num, key3Num: key3Num, key4Num: key4Num)
let borderLayer = CAShapeLayer()
borderLayer.zPosition = 4
borderLayer.path = self.borderPath.cgPath
borderLayer.fillColor = UIColor.clear.cgColor
borderLayer.strokeColor = UIColor.red.cgColor
borderLayer.lineWidth = 3.0
self.borderLayer = borderLayer
self.layer.addSublayer(self.borderLayer)
}