pkc
pkc

Reputation: 8516

How to create one sided arrowed label

I am not looking out for code snippet. I am just curious to know how to develop a UI component which is shown below. I have multiple thoughts on creating it, but would love to know the best approach

What will be the good approach to develop it. Any suggestions please.

enter image description here

Upvotes: 1

Views: 73

Answers (1)

rob mayoff
rob mayoff

Reputation: 385970

You want to display a single line of text. You can use a UILabel for that.

You have a shape you want to fill. You can use a CAShapeLayer for that. It's best to wrap the shape layer in a UIView subclass so that UIKit can lay it out properly.

You want to put the text over the shape, so use a parent view to combine the label and the shape as subviews.

import UIKit

class TagView: UIView {

    init() {
        super.init(frame: .zero)
        addSubview(background)
        addSubview(label)

        background.translatesAutoresizingMaskIntoConstraints = false
        label.translatesAutoresizingMaskIntoConstraints = false

        label.setContentHuggingPriority(.defaultHigh + 10, for: .horizontal)
        label.setContentHuggingPriority(.defaultHigh + 10, for: .vertical)

        NSLayoutConstraint.activate([
            background.heightAnchor.constraint(equalTo: label.heightAnchor, constant: 4),
            background.centerYAnchor.constraint(equalTo: label.centerYAnchor),
            background.widthAnchor.constraint(equalTo: label.widthAnchor, constant: 20),
            background.centerXAnchor.constraint(equalTo: label.centerXAnchor, constant: -2),
            leadingAnchor.constraint(equalTo: background.leadingAnchor),
            trailingAnchor.constraint(equalTo: background.trailingAnchor),
            topAnchor.constraint(equalTo: background.topAnchor),
            bottomAnchor.constraint(equalTo: background.bottomAnchor),
            ])
    }

    let label = UILabel()

    private let background = BackgroundView()

    private class BackgroundView: UIView {
        override class var layerClass: AnyClass { return CAShapeLayer.self }

        override func layoutSubviews() {
            super.layoutSubviews()
            layoutShape()
        }

        private func layoutShape() {
            let layer = self.layer as! CAShapeLayer
            layer.fillColor = #colorLiteral(red: 0.731529057, green: 0.8821037412, blue: 0.9403864741, alpha: 1)
            layer.strokeColor = nil

            let bounds = self.bounds
            let h2 = bounds.height / 2
            let path = UIBezierPath()
            path.move(to: CGPoint(x: 0, y: h2))
            path.addLine(to: CGPoint(x: h2, y: 0))
            path.addLine(to: CGPoint(x: bounds.maxX - h2, y: 0))
            path.addArc(withCenter: CGPoint(x: bounds.maxX - h2, y: h2), radius: h2, startAngle: -.pi/2, endAngle: .pi/2, clockwise: true)
            path.addLine(to: CGPoint(x: h2, y: bounds.maxY))
            path.close()

            layer.path = path.cgPath
        }
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

import PlaygroundSupport
let root = UIView(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
root.backgroundColor = .white
PlaygroundPage.current.liveView = root

let tag = TagView()
tag.translatesAutoresizingMaskIntoConstraints = false
tag.label.text = "CURRENT"
root.addSubview(tag)
NSLayoutConstraint.activate([
    tag.centerXAnchor.constraint(equalTo: root.centerXAnchor),
    tag.centerYAnchor.constraint(equalTo: root.centerYAnchor),
])

Result:

playground result

Upvotes: 3

Related Questions