reona.dev

タグを自動的に改行させる


はじめに

SwiftUI でタグを配置する際に詰まったため備忘録。

実現したいこと

↑ こういうやつ

詰まった部分

import SwiftUI
 
struct ContentView: View {
  @State var tagList = [
  - tagATagATagATagATagATagA,
  - tagBTagBTagBTagBTagBTagB,
  - tagCTagCTagCTagCTagCTagC,
  - tagDTagDTagDTagDTagDTagD]
 
  var body: some View {
    VStack {
      Text(
  - タグを横並びにしたいので HStack を使ってみると各タグが全て改行されてしまい不自然)
       .padding(.vertical, 10)
      HStack {
        ForEach(tagList, id: \.self) { tag in
          Text(tag)
        }
      }
      Spacer()
    }
  }
}

解決方法

こちらの StackOverflow の記事が参考になった。

SwiftUI HStack with Wrap

import SwiftUI
 
struct ContentView: View {
  @State var tagList = [
  - tagATagATagATagA,
  - tagBTagBTagBTagB,
  - tagCTagCTagCTagC,
  - tagDTagDTagDTagDTagDTagD]
 
  var body: some View {
    generateTags()
  }
 
  private func generateTags() -> some View {
    var width = CGFloat.zero
    var height = CGFloat.zero
 
    return ZStack(alignment: .topLeading) {
      ForEach(tagList, id: \.self) { tag in
        item(for: tag)
          .padding([.horizontal, .vertical], 4)
          .alignmentGuide(.leading, computeValue: { d in
            if abs(width - d.width) > 330 {
              width = 0
              height -= d.height
            }
            let result = width
            if tag == tagList.last {
              width = 0
            } else {
              width -= d.width
            }
            return result
          })
          .alignmentGuide(.top, computeValue: { _ in
            let result = height
            if tag == tagList.last {
              height = 0
            }
            return result
          })
      }
    }
  }
 
  func item(for text: String) -> some View {
    Text(text)
      .padding(.all, 5)
      .font(.footnote)
      .background(Color.blue)
      .foregroundColor(Color.white)
      .cornerRadius(30)
  }
}

alignmentGuide で横幅を指定することで、タグを自動的に改行する。

元記事では GeometryReader を使って端末の画面サイズを指定していたが、他のコードに影響がありレイアウトが崩れたため、330 にサイズを限定した。

また、実際にはサーバーの API からタグの配列を取得して表示させる。