Help us understand the problem. What is going on with this article?

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

More than 3 years have passed since last update.

UILabelを利用して、↓の写真のようなタグっぽい見た目のviewを実装する。
スクリーンショット 2015-12-02 23.48.13.png
ざっくり言ってしまえば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
    }

}

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

スクリーンショット 2015-12-03 0.49.35.png

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

今度はこのタグを特定のエリアに複数並べる。
タグとなるキーワード群を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
    }
}

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

スクリーンショット 2015-12-03 13.53.13.png

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away