SwiftUIでジェスチャーを使用する方法
How to use gestures in SwiftUI
gesture sequencing
ジェスチャシーケンス
DragGesture
LongPressGesture(プレスの最小継続時間を指定して、特定の秒数が経過した後にのみアクションクロージャがトリガーされる minimumDuration: 2)
onTapGesture(count、これらにパラメーターを渡して、ダブルタップ、トリプルタップを処理できる)
highPriorityGesture
simultaneousGesture
RotationGesture
MagnificationGesture
DragGesture、LongPressGesture、MagnificationGesture、RotationGesture、とTapGesture。これらには通常onEnded()、多くの場合特別な修飾子があり、onChanged()ジェスチャーが処理中(の場合onChanged())または完了した場合(の場合onEnded())にアクションを実行できます。
変更ボタンを押すとすぐに、パラメーターがtrueに設定された状態で変更クロージャーが呼び出されます。
ジェスチャが認識される前に離すと(つまり、2秒の認識機能を使用して1秒後に離すと)、変更クロージャが呼び出され、パラメータがfalseに設定されます。
レコグナイザの全長を押し続けると、パラメータがfalseに設定された状態で変更クロージャが呼び出され(ジェスチャが実行されなくなったため)、完了クロージャも呼び出されます。
Text("Hello, World!") .onLongPressGesture(minimumDuration: 1, pressing: { inProgress in print("In progress: \(inProgress)!") }) { print("Long pressed!") }
import SwiftUI struct ContentView: View { // how far the circle has been dragged @State private var offset = CGSize.zero // whether it is currently being dragged or not @State private var isDragging = false var body: some View { // a drag gesture that updates offset and isDragging as it moves around let dragGesture = DragGesture() .onChanged { value in self.offset = value.translation } .onEnded { _ in withAnimation { self.offset = .zero self.isDragging = false } } // a long press gesture that enables isDragging let pressGesture = LongPressGesture() .onEnded { value in withAnimation { self.isDragging = true } } // a combined gesture that forces the user to long press then drag let combined = pressGesture.sequenced(before: dragGesture) // a 64x64 circle that scales up when it's dragged, sets its offset to whatever we had back from the drag gesture, and uses our combined gesture return Circle() .fill(Color.red) .frame(width: 64, height: 64) .scaleEffect(isDragging ? 1.5 : 1) .offset(offset) .gesture(combined) } } /* struct ContentView: View { var body: some View { // Text("Hello, World!") // .onTapGesture(count: 2) { // print("Double tapped!") // } // Text("Hello, World!") // .onLongPressGesture { // print("Long pressed!") // } // Text("Hello, World!") // .onLongPressGesture(minimumDuration: 2) { // print("Long pressed!") // } Text("Hello, World!") .onLongPressGesture(minimumDuration: 1, pressing: { inProgress in print("In progress: \(inProgress)!") }) { print("Long pressed!") } } } */
UINotificationFeedbackGeneratorとコアハプティクスを使用して振動を作成する
Making vibrations with UINotificationFeedbackGenerator and Core Haptics
Core Haptics
「ハプティックス」はデバイスに小さなモーターを使用して、タップや振動などの感覚を作り出します。
import SwiftUI import CoreHaptics struct ContentView: View { @State private var engine: CHHapticEngine? //-------------------- func prepareHaptics() { guard CHHapticEngine.capabilitiesForHardware().supportsHaptics else { return } do { self.engine = try CHHapticEngine() try engine?.start() } catch { print("There was an error creating the engine: \(error.localizedDescription)") } } //-------------------- func complexSuccess() { // make sure that the device supports haptics guard CHHapticEngine.capabilitiesForHardware().supportsHaptics else { return } var events = [CHHapticEvent]() // create one intense, sharp tap // let intensity = CHHapticEventParameter(parameterID: .hapticIntensity, value: 1) // let sharpness = CHHapticEventParameter(parameterID: .hapticSharpness, value: 1) // let event = CHHapticEvent(eventType: .hapticTransient, parameters: [intensity, sharpness], relativeTime: 0) //events.append(event) for i in stride(from: 0, to: 1, by: 0.1) { let intensity = CHHapticEventParameter(parameterID: .hapticIntensity, value: Float(1 - i)) let sharpness = CHHapticEventParameter(parameterID: .hapticSharpness, value: Float(1 - i)) let event = CHHapticEvent(eventType: .hapticTransient, parameters: [intensity, sharpness], relativeTime: 1 + i) events.append(event) } // convert those events into a pattern and play it immediately do { let pattern = try CHHapticPattern(events: events, parameters: []) let player = try engine?.makePlayer(with: pattern) try player?.start(atTime: 0) } catch { print("Failed to play pattern: \(error.localizedDescription).") } } //-------------------- func simpleSuccess() { let generator = UINotificationFeedbackGenerator() generator.notificationOccurred(.error) } //-------------------- var body: some View { // Text("Hello, World!") // .onTapGesture(perform: simpleSuccess) Text("Hello, World!") .onAppear(perform: prepareHaptics) .onTapGesture(perform: complexSuccess) } }
allowHitTesting()を使用したユーザー対話性の無効化
Disabling user interactivity with allowsHitTesting()
allowsHitTesting
contentShape(Rectangle())
SwiftUIには、ビューのフレームとそのコンテンツの両方を使用する高度なヒットテストアルゴリズムがあります。たとえば、テキストビューにタップジェスチャーを追加すると、テキストビューのすべての部分がタップ可能になります。スペースがある場所を正確に押すと、テキストをタップできません。一方、同じジェスチャーを円にアタッチすると、SwiftUI は円の透明部分を無視します。
import SwiftUI struct ContentView: View { //-------------------- var body: some View { VStack { Text("Hello") Spacer().frame(height: 100) Text("World") } //.contentShape(Rectangle()) .onTapGesture { print("VStack tapped!") } /* Circle() .fill(Color.red) .frame(width: 300, height: 300) .contentShape(Rectangle()) .onTapGesture { print("Circle tapped!") } */ /* ZStack { Rectangle() .fill(Color.blue) .frame(width: 300, height: 300) .onTapGesture { print("Rectangle tapped!") } Circle() .fill(Color.red) .frame(width: 300, height: 300) .onTapGesture { print("Circle tapped!") } .allowsHitTesting(false) } */ } }