【SwiftUI】Search TextField を作る

これ。

NavigationStack なしでも、

自在にどこでも置きたいのだが。

👉 SwiftUIでSearchBarが使いたいので自作する | DevelopersIO hatena-bookmark

私も作ってみます、

リストが表示されるやつ。

とりあえず、横に並べて開始します。


HStack {
  Button {
  } label : {
    Image(systemName: "magnifyingglass")
  }

  TextField("Search", text: .constant(""))

  Button {
  } label : {
    Image(systemName: "xmark.circle.fill")
  }
}

 

🧑🏻‍💻 フォーカスの位置の付け替え

デフォルトの TextField のフォーカスの状態を取得して、

エフェクトは無効化する。


TextField("Search", text: .constant(""))
  .focused($focused) 
  .textFieldStyle(.plain)

それらの外にある HStack の border をフォーカス状態によって色を切り替える。


}  // HStack
.border(focused ? Color.accentColor : .gray, width: 1)

 

🧑🏻‍💻 リストのオーバーラップ表示

フォーカスの状態によって、

選択肢のリスト表示を overlay します。

offset 値は入力エリアの高さです。


}  // HStack
.border(focused ? Color.accentColor : .gray, width: 1)
.overlay {
  if focused {
    VStack {
      ForEach(0...3, id: \.self) { i in
        HStack {
          Text("\(i)")
            Spacer()
        }
      }
    }
    .background()
    .offset(y: -45)
  }
}

あとは、レイアウトを調整していけば OK!



 

🧑🏻‍💻 フォーカスのアニメーション

よく見てみると、フォーカスってアニメーションだったんですか !

フォーカス処理部分をバックグラウンドでアニメ化しました。


.background {
  RoundedRectangle(cornerRadius: 8)
    .stroke(.gray, lineWidth: 0.5)
  RoundedRectangle(cornerRadius: 8)
    .stroke(
      focused ? .orange : .clear,
      lineWidth: focused ? 5 : 16
    )
}
.animation(.default, value: focused)

 

🧑🏻‍💻 まとめ



もうググってもしんどいですね。

ChatGPT や copilot などのAI系でも時間がかかるし。

👉 【SwiftUI】#Preview with @Binding arguments hatena-bookmark



【SwiftUI】View の overlay は タップイベント を透過できるのか

なかなか意図通りに動かないので、シンプルに順番に試してみます。

まず、シンプルなボタン。


Button("orange") {
  print("clicked.") 
}

// → イベントは反応する

次は、オレンジ色を overlay します。


Button("orange") {
  print("clicked.") 
}
.overlay {
  Color.orange
}

// → 透過できない

上に被せると透過しないのか。

では、無色で。


Button("clear") {
  print("clicked.") 
}
.overlay {
  Color.clear
}

// → 透過できる

なるほど、無色の Color.clear は透過するのか。

では、opacity を付けると、


Button("orange - 0.5") {
  print("clicked.") 
}
.overlay {
  Color.orange
    .opacity(0.5)
}

// → 透過できない

なるほど、すなわち有色は透過しないということか。

続いて、overlay 側に frame でサイズ指定してみる。


Button("orange - 200x50 - 0.5") {
  print("clicked.") 
}
.overlay {
  Color.orange
    .frame(width: 200, height: 50)
    .opacity(0.5)
}

// → 透過できる

なんで透過するの ?

続いて、opacity を 0.6 に。


Button("orange - 200x50 - 0.6") {
  print("clicked.")
}
.overlay {
  Color.orange
    .frame(width: 200, height: 50)
    .opacity(0.6)
}

// → 透過できない

opacity も影響はする。

なら、modifier の順序を変えてみる。


Button("orange - 0.5 - 200x50") {
  print("clicked.")
}
.overlay {
  Color.orange
    .opacity(0.5)
    .frame(width: 200, height: 50)
}

// → 透過できない

...。

 

🤔 まとめ

どう考えたらいいのか分かりません !

同じ SwiftUI でも iOS と macOS で挙動も変わる模様。

本当は、ZStack や zIndex なども比較したかったのですが折れました。


【SwiftUI】View のロジック部分を外出しにする構成

ModelView を作成することで

「ロジック部分を外出し」

して View の見通しを良くすることができます。

同時に @State も外部クラスに任せますか。


@Observable final class StateHolder {
  var text = ""
}


var stateHolder = StateHolder()

TextField("text", text: Binding(
  get: { stateHolder.text },
  set: { newValue in stateHolder.text = newValue })
)


var stateHolder = StateHolder()

@Bindable var sh = stateHolder
TextField("text", text: $sh.text)

複雑なデータ処理ロジック部分も置いてしまうと、

実質の今どきの ModelView となりますね。

基本、ModelView は View に対して 1:1 なことを忘れないでください。



👉 【SwiftUI】@State と 単方向データフロー hatena-bookmark
👉 【SwiftUI】ModelView は 単なる StateHolder なのか ? - ModelView の役割 hatena-bookmark