IT アプリ開発

SwiftUIの定義を読む #DragGesture

2022年1月16日

DragGesture

@available(iOS 13.0, macOS 10.15, watchOS 6.0, *)
@available(tvOS, unavailable)
public struct DragGesture : Gesture {

    public struct Value : Equatable {

        public var time: Date

        public var location: CGPoint

        public var startLocation: CGPoint

        public var translation: CGSize { get }

        public var predictedEndLocation: CGPoint { get }

        public var predictedEndTranslation: CGSize { get }

        public static func == (a: DragGesture.Value, b: DragGesture.Value) -> Bool
    }

    public var minimumDistance: CGFloat

    public var coordinateSpace: CoordinateSpace

    public init(minimumDistance: CGFloat = 10, coordinateSpace: CoordinateSpace = .local)

    public typealias Body = Never
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
public protocol Gesture {

    associatedtype Value

    associatedtype Body : Gesture

    var body: Self.Body { get }
}

@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
extension Gesture {

    public func onEnded(_ action: @escaping (Self.Value) -> Void) -> _EndedGesture< Self >
}

@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
extension Gesture where Self.Value : Equatable {

    public func onChanged(_ action: @escaping (Self.Value) -> Void) -> _ChangedGesture< Self >
}

DragGestureのinitializeを見ると、お馴染みのminimumDistanceとcoordinateSpaceがある
デフォルトで10、.localとあるので特に引数指定しなくても問題なし。

   DragGesture()

DragGestureはGestureプロトコルを継承している。
そのため、Gestureプロトコルに定義しているメソッドなどを使用することができるわけで、
定義を見ると、onEndedやonChangeメソッドが定義されている。

 

public func onEnded(_ action: @escaping (Self.Value) -> Void)
public func onChanged(_ action: @escaping (Self.Value) -> Void)

 

それぞれ、メソッドの定義としては、Self.ValueつまりGestureプロトコルのValueプロパティ情報を受け取り、戻り値はVoidなので、
valueの値を受け取りつつ、その値で何らかの処理を実行するイメージにある。戻り値はないので、特に返すなどせず、
ドラッグしている間とか、最後にどんな処理をするかを記載することになる。

   DragGesture().onChanged({ val in

   })

 

さらに、onChangedやonEndedは、メソッドの戻り値がselfつまり、Gesture型になるので、
メソッドチェーンのように書ける。

DragGesture()
  .onChanged({ val in

  })
  .onEnded({ val in

  })

さらに、DragGestureはViewプロトコルではないので、
some view{}にはかけません。
次にどこに記載するかについて話したいと思います。

 

DragGestureはGestureプロトコルなのでどこに記載する

以下のような最初のコードのsome Viewの中には直で記載できない。
DragGesture()自体は、ViewではなくGestureプロトコルを返すため、エラーになる。

struct ContentView:View{
  var body: some View {

  }
}

ではどうするか?
以下2つの書き方があるかなと思います。

  • 1. var gesture: some Gesture{}のように記載する。
  • 2. モディファイアgestureに記載する。

 

1. var gesture: some Gesture{}のように記載する。

struct DragGestureView: View {
   @State var isDragging = false

   var drag: some Gesture {
       DragGesture()
           .onChanged { _ in self.isDragging = true }
           .onEnded { _ in self.isDragging = false }
   }

   var body: some View {
       Circle()
           .fill(self.isDragging ? Color.red : Color.blue)
           .frame(width: 100, height: 100, alignment: .center)
           .gesture(drag)
   }
}

上はswiftuiの定義コードですが、
GestureとViewの部分を分けて、作成しています。
 

2. モディファイアgestureに記載する。

.gestureモディファイアの定義を見ると、戻り値はGesture。
なので、DragGesture()を指定することが可能。

@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
extension View {

    public func gesture< T >(_ gesture: T, including mask: GestureMask = .all) -> some View where T : Gesture
}

ジェネリクスにはwhereから、Gestureプロトコルに従っている型なので、DragGesture型を採用できる。これにより、以下のようにかける。DragGesture型でreturnしているのでOK

Image("apple")
  .gesture(
      DragGesture().onChanged { value in
        // ドラッグ中の処理
        self.location = "\(value.location)"
        self.translation = "\(value.translation)"
      }.onEnded { value in
        // ドラッグ終了時の処理
        self.location = "\(value.location)"
        self.translation = "\(value.translation)"
      }
  }

Value

プロパティ 説明
time ドラッグジェスチャの現在のイベントに関連付けられた時間。
location ドラッグジェスチャの現在のイベントの位置。
startLocation ドラッグジェスチャーの最初のイベントの位置。
translation ドラッグジェスチャの開始からイベントまでの合計移動量。
ドラッグジェスチャの現在のイベント。
これは `location.{x,y} - startLocation.{x,y}` と等価である。
predictedEndLocation 現在のドラッグ速度に基づく、最終的な位置の予測。
今ドラッグを止めた場合の位置。
predictedEndTranslation 現在のドラッグ速度から予測される、最終的なもし今ドラッグを止めた場合

-IT, アプリ開発
-,

© 2024 Yosshi Labo. Powered by AFFINGER5