[SwiftUI]UIをタップ、長押しした時の処理

目次

概要

タップした時、長押しした時に処理を行いたいことはあるが、
例えば、グリッド上に表示されているビューをタップした時、タップではなく長押しした時に処理を行う時など。
今回はそのボタン以外でタップ、長押しをする時の実装方法を記載する。

ソースコード

アプリの最初の実行部分

import SwiftUI

@main
struct TestApp: App {
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
    
    var body: some Scene {
        WindowGroup {
            TapGestureView()
        }
    }
}

View表示部分

import SwiftUI

struct TapGestureView: View {
    @ObservedObject private var viewModel = TapGestureViewModel()
    
    @State var textColor: Color = .black
    @State var iconTapCount: Int = 0
    
    let cellImage = Image(systemName: "bell.square")
    var gradientLayer = CAGradientLayer()
    
    var body: some View {
        VStack {
            Spacer()
            Text("セルを表示しているよ")
                .frame(width: 240, height: 100)
                .border(textColor)
                .foregroundColor(textColor)
                .onTapGesture {
                    iconTapCount += 1
                    switch iconTapCount % 5 {
                    case 0:
                        textColor = .red
                    case 1:
                        textColor = .blue
                    case 2:
                        textColor = .green
                    case 3:
                        textColor = .yellow
                    case 4:
                        textColor = .black
                    default:
                        textColor = .black
                    }
                }
            Spacer()
            cellImage
                .resizable(resizingMode: .stretch)
                .frame(width: 100, height: 100)
                .foregroundColor(viewModel.imageColor)
                .onLongPressGesture(minimumDuration: 1.0, maximumDistance: 20) {
                    print("長押し処理")
                    viewModel.timerFiring()
                } onPressingChanged: { isPress in
                    if isPress {
                        print("押されてからminimumDurationの時間が経つまで")
                        viewModel.stopTimer()
                    } else {
                        print("minimumDurationだけ時間が経った")
                    }
                }
                
            Spacer()
        }
    }
}

処理部分

import Foundation
import SwiftUI

class TapGestureViewModel: ObservableObject {
    var red: Double = 1.0
    var green: Double = 0.0
    var blue: Double = 0.0
    var count: Int = 0
    var timer: Timer?
    
    @Published private(set) var imageColor: Color = Color.init(red: 1.0, green: 0.0, blue: 0.0)
    
    func timerFiring() {
        timer = Timer.scheduledTimer(timeInterval: 0.01, target: self, selector: #selector(updateColor), userInfo: nil, repeats: true)
    }

    @objc private func updateColor() {
        count = (count + 1) % 300
        switch count {
        case 0 ..< 50:
            red = 1.0
            green = 0.0
            blue = 0.0 + Double(count)/50.0
        case 50 ..< 100:
            red = 1.0 - Double(count-50)/50.0
            green = 0.0
            blue = 1.0
        case 100 ..< 150:
            red = 0.0
            green = 0.0 + Double(count-100)/50.0
            blue = 1.0
        case 150 ..< 200:
            red = 0.0
            green = 1.0
            blue = 1.0 - Double(count-150)/50.0
        case 200 ..< 250:
            red = 0.0 + Double(count-200)/50.0
            green = 1.0
            blue = 0.0
        case 250 ..< 300:
            red = 1.0
            green = 1.0 - Double(count-250)/50.0
            blue = 0.0
        default:
            red = 0.0
            green = 0.0
            blue = 0.0
        }
        imageColor = Color(red: red, green: green, blue: blue)
    }
    
    func stopTimer() {
        timer?.invalidate()
    }
}

デモ動画

以下の動画は、「セルを表示しているよ」の部分をタップすると色が変わり、
下の「ベルマーク」を長押しすると色が変わるというもの

詳細

タップした時の処理を実装

Viewなど、ボタン以外のタップをした時の処理の実装方法

.onTapGesture {
    // UIをタップされた時に行われる処理
}

長押しした時に表示されるメニュー

長押しした時に行う処理の実装方法

.onLongPressGesture {
    // 長押しをした時に行われる処理
}

また、ソースコードに書かれたもののように、onLongPressGestureには以下の設定値も設定することができる。最低限設定する必要があるのがperform actionのみ。

引数名内容
minimumDurationUIをタップしてから長押しの処理を行うまでの時間
Double型でデフォルトは0.5
maximumDistanceUIをタップしたところを中心に動かしてもいい範囲
CGFloat型でデフォルトは10
perform action(必須)(() -> Void)型で長押ししている時の処理内容
onPressingChanged((Bool) -> Void)?型でデフォルトはnil
isPressがtrue
→UIが押されてからminimumDurationの時間が経つまで
isPressがfalse
→UIが押されてからminimumDurationの時間が経った時

参考ページ
iOS アプリ開発「[SwiftUI] タップや長押しの認識、onTapGestureとonLongPressGesture」
Qiita「Swift4環境でTimerの使い方と挙動を確認してみた」