Swift TextViewでのタグ入力UIを作った


概要

こういうインターフェースを作ったので記録。

こういうやつ

実装

UILabelのタグ風の加工については省略。この記事を参考にすればいいと思う。

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

最初はセパレーター文字の入力を契機にUILabelをaddSubViewしていたんだけど、delete keyの入力があるとタグを削除するロジックがしんどくなるので、入力の度にViewを破棄、再生成するような処理に変更した。1文字入力される度にタグの数だけViewを再生成するのでちょっと微妙かも...

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var textView: UITextView!
    //タグのposition
    var tagsPositionX:CGFloat = 0
    var tagsPositionY:CGFloat = 0
    var tagLabels = [UILabel]()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        textView.delegate = self   
    }
    
    //タグLabelの生成
    private func makeTagLabel(_ value:String){
        let tagLabel = UILabel(frame: CGRect(x: self.textView.frame.origin.x + tagsPositionX, y: self.tagsPositionY, width: 0, height: 0))
        tagLabel.text = value
        tagLabel.sizeToFit()

        //タグが親Viewからはみ出す時はy座標をタグheight分ずらす
        if( tagLabel.frame.maxX >= self.view.frame.size.width){
            self.tagsPositionY = tagLabel.frame.maxY + 5
            tagLabel.frame.origin.y = self.tagsPositionY
            //x座標は開始位置
            self.tagsPositionX = 0
            tagLabel.frame.origin.x = self.textView.frame.origin.x + tagsPositionX
        }
        //次のlabelのx座標を配置したlabelのwidth分ずらす
        self.tagsPositionX = tagLabel.frame.maxX + 5
        self.tagLabels.append(tagLabel)
        self.view.addSubview(tagLabel)
    }
}

extension ViewController: UITextViewDelegate{
    func textViewDidChange(_ textView: UITextView) {
        self.textView.textViewDidChange(textView)
        let text = textView.text!
        //改行も空白とみなす
        let replaceText = text.replacingOccurrences(of: "\n", with: " ")

        let splitTags = replaceText.split(separator: " ")

        //作成、配置済みのlabelをすべて剥がす
        self.tagLabels.forEach { (tagLabel) in
            tagLabel.removeFromSuperview()
        }
        self.tagLabels.removeAll()
        //配置位置の初期化
        self.tagsPositionX = 0
        self.tagsPositionY = self.textView.frame.maxY + 10
        //labelを再生成する
        splitTags.forEach { (value) in
            self.makeTagLabel(String(value))
        }
    }
}