【SwiftUI】View を ドラッグ して移動 する

View をドラッグして動かします。


struct TestDragGesture: View {
  @State private var location = CGPoint(x: 150, y: 150)

  var body: some View {
    ZStack {
      VStack(alignment: .trailing) {
        Text("location.x: \(location.x)")
        Text("location.y: \(location.y)")
      }
      .monospaced()

      Circle()
        .fill(.red.opacity(0.5))
        .frame(width: 100)
        .position(location)
        .gesture(
          DragGesture()
            .onChanged { value in
              location = value.location
            }
        )
    }
  }
}

ドラッグ位置が変化するたびに View の位置を更新できます。

var location: CGPoint
The location of the drag gesture’s current event.

👉 DragGesture.Value | Apple Developer Documentation hatena-bookmark

Circle の position は Viewの中心

であることに対して

DragGesture().onChanged() で取得したドラッグ位置

とずれがあるため、

タップ位置を動かした瞬間に中心位置にジャンプしてしまいます。

なんか違和感がありますね !

 

👩🏼‍💻 startLocation と translation

計算方法を変えます。

以下を使います。

var startLocation: CGPoint
The location of the drag gesture’s first event.

var translation: CGSize
The total translation from the start of the drag gesture to the current event of the drag gesture.

👉 DragGesture.Value | Apple Developer Documentation hatena-bookmark


Circle()
  .fill(.red.opacity(0.5))
  .frame(width: 100)
  .position(location)
  .gesture(
    DragGesture()
      .onChanged { value in
        var newLocation = startLocation ?? location
        newLocation.x += value.translation.width
        newLocation.y += value.translation.height
        location = newLocation
      }
      .updating($startLocation) { _, startLocation, _ in
        startLocation = startLocation ?? location
      }
   )

Circle の中心位置に対して、ドラッグの移動量を増減することで、なめらかに違和感なく動くようになりました !



簡単なコードに見えますが、updating を使った位置情報の入れ替えとか、よくみると深い。

👉 updating(_:body:) | Apple Developer Documentation hatena-bookmark
👉 Move your view around with Drag Gesture in SwiftUI | Sarunw hatena-bookmark

しかし、なんか見通しが悪い。

@GestureState と updating() は使いづらい。

 

👩🏼‍💻 まとめ

これでいきます。



キモは、

「ドラッグの移動量を View のポジションに適用」

「ドラッグ開始時のドラッグ位置とView中心のズレ」



【SwiftUI】TextField の角を丸くして背景色を付けるもっとも簡単な方法は

これ簡単に作る方法ありませんかね。

角の丸い背景に色を付けた TextField。

なんか簡単にできないんですが、

いい方法ありませんか。

 

🧑‍💻 やってみる

「角を丸く」

「背景色を黄色に」

が同時にできません。


TextField("Search", text: $text)
  .frame(width: 200)
  .background(.yellow)

TextField("Search", text: $text)
  .frame(width: 200)
  .textFieldStyle(.roundedBorder)
  .background(.yellow) // NG
  .backgroundStyle(.yellow) // NG
  .tint(.yellow) // NG

👉 roundedBorder | Apple Developer Documentation hatena-bookmark

 

🧑‍💻 Chain Modifiers

一度、スタイルを plain にして、

padding を付けて、

背景に黄色い角丸四角を置きます。


// chain view modifiers
TextField("Search", text: $text)
  .textFieldStyle(.plain)
  .padding(6)
  .background(.yellow, in: .rect(cornerRadius: 6))
  .frame(width: 200)

しかし、数行続くと View 全体の見通しが悪くなるのがいやだ。

 

🧑‍💻 Create TextFieldStyle

TextField 専用のスタイルを作ってそれを適用します。


struct RoundedBordertFieldStyle: TextFieldStyle {
  var cornerRadius: CGFloat
  var color: Color

  func _body(configuration: TextField<Self._Label>) -> some View {
    configuration
      .textFieldStyle(.plain) // macOS
      .padding(cornerRadius)
      .background(color, in: .rect(cornerRadius: cornerRadius))
  }
}

extension TextFieldStyle where Self == RoundedBordertFieldStyle {
  static var roundedBorderYellow: Self {
    Self(cornerRadius: 6, color: .yellow)
  }
}


TextField("Search", text: $text)
  .textFieldStyle(RoundedBordertFieldStyle(cornerRadius: 6, color: .yellow)) // OK
  //.textFieldStyle(.roundedBorderYellow) // OK
  .frame(width: 200)

しかし、いちいちここまで書くのがいやだ。

_ (アンダースコア) もなんかいや。

👉 TextFieldStyle Protocol "makeBody" method hidden | Apple Developer Forums hatena-bookmark

 

🧑‍💻 Create View extension

ViewModifier を作ろうかと思ったが、

View の extension まででいいですよね。


extension View {
  func roundedBorder(cornerRadius: CGFloat, color: Color) -> some View {
    self
      .textFieldStyle(.plain)
      .padding(cornerRadius)
      .background(color, in: .rect(cornerRadius: cornerRadius))
  }
}


// create view extension (or view modifier)
TextField("Search", text: $text)
  .roundedBorder(cornerRadius: 6, color: .yellow)
  .frame(width: 200)

これぐらいがいいのかな。

 

🧑‍💻 Create Child View

新しく View を作って、それに押し込んじゃいます。


struct RoundedBorderTextField: View {
  var label: String
  @Binding var text: String
  var cornerRadius: CGFloat
  var color: Color

  var body: some View {
    TextField(label, text: $text)
      .textFieldStyle(.plain)
      .padding(cornerRadius)
      .background(color, in: .rect(cornerRadius: cornerRadius))
  }
}


// create child view
RoundedBorderTextField(label: "Search", text: $text, cornerRadius: 6, color: .yellow)
  .frame(width: 200)

最初からこれで良かったのでは。

 

🧑‍💻 まとめ

どれが一番いいんすかね。

macOS に切り替えなが思ったのは、

実際、TextField って、

「フォーカス」とかも

取り扱ってますよね。

それを考えると、

「@State を考慮してるやつ」

のがいいのかもしれません。

👉 【SwiftUI】市松模様を背景にする - Checkered Pattern Background 🏁 hatena-bookmark
👉 【SwiftUI】枠線付き角丸ボタンを簡単に作りたい hatena-bookmark



【SwiftUI】Color とは一体何なのか

ふと思ったのですが。


Color
  .red
  .frame(width: 200, height: 200)

ただの「色」なのに、

「縦横寸法が指定できる」

「枠や対象がないのに表示される」

なんだか気持ち悪くないですか。

一体何者なんだ。

 

🎨 何者であるかの種類

思いつくものを、いくつか挙げておきます。

このうちのどれかだと思います。

View
A type that represents part of your app’s user interface and provides modifiers that you use to configure views.
アプリのユーザーインターフェイスの一部を表し、ビューの設定に使用する修飾子を提供するタイプ。

👉 View | Apple Developer Documentation hatena-bookmark

Shape
A 2D shape that you can use when drawing a view.
ビューを描くときに使用できる2D形状。

👉 Shape | Apple Developer Documentation hatena-bookmark

ShapeStyle
A color or pattern to use when rendering a shape.
図形をレンダリングするときに使用する色またはパターン。

👉 ShapeStyle | Apple Developer Documentation hatena-bookmark

眺めてるとなんとなくわかります。

何者かの種類のことが

「Protocol」

だったのですね !

 

🎨 よく使うあれは何者なのか

ドキュメントの「Conforms To」のところを見ます。

よく使うやつを見てみます。

Text
A view that displays one or more lines of read-only text.


Conforms To
Equatable
Sendable
View // *

👉 Text | Apple Developer Documentation hatena-bookmark

→ Text は View。

Button
A control that initiates an action.


Conforms To
View // *

👉 Button | Apple Developer Documentation hatena-bookmark

→ Button は View。

VStack
A view that arranges its subviews in a vertical line.


Conforms To
View // *

👉 VStack | Apple Developer Documentation hatena-bookmark

→ VStack は View。

Circle
A circle centered on the frame of the view containing it.


Conforms To
Animatable
ChartSymbolShape
InsettableShape
Sendable
Shape // *
View // *

👉 Circle | Apple Developer Documentation hatena-bookmark

→ Circle は Shape であり、View でもある。

RoundedRectangle
A rectangular shape with rounded corners, aligned inside the frame of the view containing it.


Conforms To
Animatable
InsettableShape
Sendable
Shape // *
View // *

👉 RoundedRectangle | Apple Developer Documentation hatena-bookmark

→ RoundRectangle は Shape であり、View でもある。

Material
A background material type.


Conforms To
Sendable
ShapeStyle // *

👉 Material | Apple Developer Documentation hatena-bookmark

→ Material は ShapeStyle である。

では、

Color
A representation of a color that adapts to a given context.


Conforms To
CustomStringConvertible
Equatable
Hashable
Sendable
ShapeStyle // *
Transferable
View // *

👉 Color | Apple Developer Documentation hatena-bookmark

→ Color は、 ShapeStyle であり、View でもある !

 

🎨 まとめ

Color は、

Material のような塗りつぶし方法の「ShapeStyle」であり、

Text のような表示の具の 「View」 でもある。

だったのだ !

👉 The many faces of ShapeStyle in SwiftUI | Swift with Majid hatena-bookmark
👉 Every SwiftUI protocol explained | FIVE STARS hatena-bookmark