UILabelでタグっぽい見た目を実装する


UILabelを利用して、↓の写真のようなタグっぽい見た目のviewを実装する。

ざっくり言ってしまえばUILabelにボーダーつけて終わり、なんですがw

まずは1個のタグ

StoryBoardにUIViewControllerをおいて、ViewController.swiftに表示内容を書いていく。
まずはタグ表示に利用するUILabelの子クラスを作成。

initでタグのボーダーや角丸の設定などを行ってしまっている。
プロパティのtagPaddingは、タグ内の文字のパディング幅。

drawRect(_:)をオーバーライドし、UIEdgeInsetsで上下左右にtagPadding分のinsetをつけてdrawTextInRect(_:)returnしている。

ViewController.swift
import UIKit

class TagLabel: UILabel {

    let tagPadding: CGFloat = 5

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

        layer.cornerRadius = 2
        layer.borderColor = UIColor.blackColor().CGColor
        layer.borderWidth = 1
        textColor = UIColor.blackColor()
        clipsToBounds = true
        numberOfLines = 1
    }

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

    override func drawRect(rect: CGRect) {
        let insets = UIEdgeInsets(top: tagPadding, left: tagPadding, bottom: tagPadding, right: tagPadding)
        return super.drawTextInRect(UIEdgeInsetsInsetRect(rect, insets))
    }

}

続いて、ViewControllerクラスの実装。

tagLabelTextWidth(text:font:height:)メソッドでタグの幅を計算する。
中でやっていることは単純。

  • 一度ダミーのインスタンス(UILabel)を生成
  • sizeToFit()してラベル幅を中のテキストぴったりに合わせ、タグの文字となる部分のwidthを算出
  • 上記の値に、左右のパディング幅を加えた値を返す

ということを行っている。
その後、viewDidLoad()内で、算出したタグの幅を利用して本物のTagLabelを作りaddSubview

ViewController.swift
class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let tagFont = UIFont.systemFontOfSize(16)
        let tagHeight = tagFont.lineHeight + TagLabel().tagPadding * 2
        let tagText = "hoge"
        let tagWidth = tagLabelTextWidth(text: tagText, font: tagFont, height: tagHeight)
        let tag = TagLabel(frame: CGRectMake(100, 100, tagWidth, tagHeight))
        tag.text = tagText
        tag.font = tagFont
        view.addSubview(tag)
    }

    func tagLabelTextWidth(text text: String, font: UIFont, height: CGFloat) -> CGFloat {
        let label = UILabel(frame: CGRectMake(0, 0, CGFloat.max, height))
        label.font = font
        label.text = text
        label.sizeToFit()
        return label.frame.size.width + TagLabel().tagPadding * 2
    }

}

以下のような表示がこれでできる。

特定のエリアに複数のタグを並べる

今度はこのタグを特定のエリアに複数並べる。
タグとなるキーワード群をwordsという文字列の配列で表現。

先ほど利用したTagLabelのインスタンスをplaceTagsOn(_:tags:)メソッドで複数個生成し、特定のUIView(下記のコード中のtagsView)にaddSubViewしていく。タグを並べる間隔は縦方向も横方向も10pxに設定。

  • 表示エリアtagsViewの中でのタグのoriginを設定するため、tagOriginX, tagOriginYを宣言
  • 先ほどと同じくタグの横幅をtagLabelTextWidth(text:font:height:)で計算
  • この横幅が表示エリアの横幅を超えていたら、超過した長さを切り捨てて表示エリアの横幅と同値に
  • 表示エリアの横方向の余白 (areaWidth - tagOriginX) がタグの横幅よりも小さい時、改行して表示したいのでtagOriginX, tagOriginYの値を変更
    • 改行して左端から表示するのでtagOriginXは0に
    • tagOriginYに、改行した分の高さ(tagHeight + tagMargin)を加える
  • tagOriginX, tagOriginYを使ってTagLabeladdSubview
  • 追加したタグ幅(tagWidth + tagMargin)の大きさを、tagOriginXに加える

placeTagsOn(_:tags:)でこれらの処理を繰り返し行うことで、複数のタグを特定のエリア内に配置している。

ViewController.swift
let words = [
    "Ruby",
    "Ruby on Rails",
    "JavaScript",
    "Swift",
    "iOS",
    "HTML",
    "CSS",
    "Perl",
    "Python",
    "PHP",
    "Java",
    "Scala",
    "Go",
    "Elixir",
    "Objective-C"
]

override func viewDidLoad() {
    super.viewDidLoad()

    // (略)

    let tagsView = UIView(frame: CGRectMake(50, 200, view.bounds.size.width - 50 * 2, 200))  // 高さは適当な大きさに
    view.addSubview(tagsView)
    placeTagsOn(tagsView, tags: words)
}

func placeTagsOn(tagsView: UIView, tags: [String]) {
    let areaWidth = tagsView.bounds.size.width
    let tagMargin: CGFloat = 10
    let tagFont = UIFont.systemFontOfSize(16)
    let tagHeight = tagFont.lineHeight + TagLabel().tagPadding * 2

    var tagOriginX: CGFloat = 0
    var tagOriginY: CGFloat = 0

    for tag in tags {
        var tagWidth = tagLabelTextWidth(text: tag, font: tagFont, height: tagHeight)
        if tagWidth > areaWidth {
            tagWidth = areaWidth
        }

        if areaWidth - tagOriginX < tagWidth {
            tagOriginX = 0
            tagOriginY += tagHeight + tagMargin
        }

        let label = TagLabel(frame: CGRectMake(tagOriginX, tagOriginY, tagWidth, tagHeight))
        label.text = tag
        label.font = tagFont
        tagsView.addSubview(label)

        tagOriginX += tagWidth + tagMargin
    }
}

表示は以下のようになり、タグが同間隔で並んでいるのがわかる。