IT アプリ開発

【SwiftUI】ドラッグで右外から別Viewを表示させる

2022年1月24日

よくWebやアプリで、右側から画面を表示するなどを見たことがあるかと思います。
右からドラッグすることでマイページが表示されたり、
ハンバーガーボタンをタップすると右からニュルッと出てきたりするものを見たことがあるかなと思います。

タップしてニュルッと出てくるものはアニメーションでドラッグみたいに距離を計算して引き算して表示。
要はタップして、1秒後に表示するとなった場合、その1ミリ秒ごとに動く距離を出してそれでoffsetで引けば移動する。
さらに、等速度でニュルッと表示だと味気ないので、amiationのkeyframeであるeaseinを用いて、
最初は早く、止まるところで遅くするようにして表示する。

1. まず土台となる2つのViewをZStackで表示

import SwiftUI

struct TestGesture: View {

    var body : some View{
        ZStack(){
            Color.blue
            Color.green
                .offset(x: 400, y: 0)   
                
            // positionがいわゆるそのviewの配置場所(viewの左上部分)となる → それがデフォルトでは枠外にあればよくて、gestureで動かした際にそれが移動すればいい!
            
        }
    }
}

画像を表示。
offsetでまず以上のように場所を指定できる。

1. まず土台となる2つのViewをZStackで表示

次に画面の右に配置する。
xには画面サイズを指定すれば、ちょうど画面外に緑viewが配置されるようになる。

import SwiftUI

struct TestGesture: View {
   
    var body : some View{
        ZStack(){
            Color.blue
            Color.green
                .offset(x: UIScreen.main.bounds.width, y: 0) 
                
            // positionがいわゆるそのviewの配置場所(viewの左上部分)となる → それがデフォルトでは枠外にあればよくて、gestureで動かした際にそれが移動すればいい!
            
        }
    }
}

UIScreen.main.bounds.widthでデバイスの画面の横幅を取得できるので、
これを指定してoffsetすることで、ちょうど画面の右外に緑Viewが配置されるので、ビルドすると以下のように青viewしか見えない状態になる。

1. まず土台となる2つのViewをZStackで表示

次に右から左へドラッグした際に、右外にある緑Viewが引っ張られる形で表示されるようにする。
ドラッグで動かすためには、その動かしたいviewに対してgestureモディファイア(gestureプロトコルを戻す)を指定する。
これによって、ドラッグをするとそのViewに変化を加えることができる。
Viewプロトコルは画面に表示するという役割があるのに対し、Gestureプロトコルはタップやスワイプ、ドラッグなどアプリ内でのジェスチャーに反応する役割がある。

戻り値としてはgestureプロトコルなので、DragGestureなどを使用することになる。
DragGestureはgestureプロトコルの構造体になるので、DragGestureを処理で加えることになる。

Dragした距離分、緑Viewを引っ張ってくる。
そして引っ張ってくるということは、緑Viewの配置をリアルタイムに変えていく必要がある。
そのため、以下を考える必要があります。

  • 緑Viewのoffsetをリアルタイムに変更する
  • @Stateを用いて、Viewの再構築を行い、リアルタイムに変更する
import SwiftUI

struct TestGesture: View {
   
    @State public var gio:CGSize = CGSize(width: 200, height: 300)
    
    @State public var x_from_dragstart:CGFloat = 0      // dragし始めたところからのxの位置
    @State public var y_from_dragstart:CGFloat = 0      // dragし始めたところからのyの位置
    
    var gesture: some Gesture{
        DragGesture()
            .onEnded{ value in
//                self.current_x = value.startLocation.x + value.translation.width     // startLocationはドラッグを始めた場所
//                self.current_y = value.startLocation.y + value.translation.height
                self.x_from_dragstart = value.translation.width
                self.y_from_dragstart = value.translation.height
                print(value.translation.width)
            }
            .onChanged { value in
//                self.current_x = value.startLocation.x + value.translation.width
//                self.current_y = value.startLocation.y + value.translation.height
                self.x_from_dragstart = value.translation.width
                self.y_from_dragstart = value.translation.height
                print(value.translation.width)
            }
    }
    
    var body : some View{
        ZStack(){
            Color.blue
            Color.green
                .offset(x: UIScreen.main.bounds.width + x_from_dragstart, y: 0) 
                .gesture(
                    self.gesture
                )
        }
    }
}

Appendix: ドラッグの距離を制御する

上で構築した画面表示ですが、
ドラッグでまた緑Viewを右外に持っていくと画面外になり、
次はドラッグしても緑Viewが出てこなくなる。
これは、もともとちょうど右外に配置していたものが、一気に外出てしまったため、ドラッグしても反映されなくなってしまったためです。
なので、以下のように、if文で制御をかけることで、
何回やっても緑Viewが出てくるようになる。

import SwiftUI

struct TestGesture: View {
   
    @State public var gio:CGSize = CGSize(width: 200, height: 300)
    
    @State public var x_from_dragstart:CGFloat = 0      // dragしたところからのxの位置
    @State public var y_from_dragstart:CGFloat = 0      // dragしたところからのyの位置
    
    var gesture: some Gesture{
        DragGesture()
            .onEnded{ value in
//                self.current_x = value.startLocation.x + value.translation.width     // startLocationはドラッグを始めた場所
//                self.current_y = value.startLocation.y + value.translation.height
                if(value.translation.width < 0){
                    self.x_from_dragstart = value.translation.width
                    self.y_from_dragstart = value.translation.height
                    print(value.translation.width)
                } else {
                    self.x_from_dragstart = 0
                    self.y_from_dragstart = 0
                    print(value.translation.width)
                }
            }
            .onChanged { value in
//                self.current_x = value.startLocation.x + value.translation.width
//                self.current_y = value.startLocation.y + value.translation.height
                if(value.translation.width < 0){
                    self.x_from_dragstart = value.translation.width
                    self.y_from_dragstart = value.translation.height
                    print(value.translation.width)
                } else {
                    self.x_from_dragstart = 0
                    self.y_from_dragstart = 0
                    print(value.translation.width)
                }
            }
    }
    
    var body : some View{
        ZStack(){
            Color.blue
            Color.green
                .offset(x: UIScreen.main.bounds.width + x_from_dragstart, y: 0) 
                .gesture(
                    self.gesture
                )
        }
    }
}

-IT, アプリ開発
-,

© 2024 Yosshi Labo. Powered by AFFINGER5