🦋
SwiftUI: iOS標準キーボードのシフトキーと同じ挙動のボタン
SwiftUIにある.onTapgesture()
モディファイアではタップとダブルタップの複雑な機能を実装することはできなかったため、DragGesture
を用いて自作しました。
iOS標準ソフトウェアキーボードの挙動
- タップで大文字/小文字入力の切り替えができる
- ダブルタップでCapsLock状態(大文字入力固定)にできる
- すでにCapsLock状態でタップをすると小文字入力になる
- すでにCapsLock状態でダブルタップをすると大文字入力になる
(ダブルタップの1度目のタップ時は小文字入力になっている)
public enum ShiftState {
case off
case on
case capsLock
var systemName: String {
switch self {
case .off:
return "shift"
case .on:
return "shift.fill"
case .capsLock:
return "capslock.fill"
}
}
・・・
}
class ShiftButtonModel: ObservableObject {
@Binding var bindingShiftState: ShiftState
@Published var shiftState: ShiftState {
didSet { bindingShiftState = shiftState }
}
private var isTouching: Bool = false
private var previousDate: Date?
init(shiftState: Binding<ShiftState>) {
_bindingShiftState = shiftState
self.shiftState = shiftState.wrappedValue
}
func touchDown() {
if isTouching { return }
isTouching = true
if let previousDate, -previousDate.timeIntervalSinceNow < 0.25 {
shiftState = (shiftState == .capsLock) ? .off : .capsLock
} else {
shiftState = (shiftState == .off) ? .on : .off
}
previousDate = Date.now
}
func touchUp() {
isTouching = false
}
}
public struct ShiftButton: View {
@StateObject var model: ShiftButtonModel
public init(shiftState: Binding<ShiftState>) {
_model = StateObject(wrappedValue: ShiftButtonModel(shiftState: shiftState))
}
public var body: some View {
Image(systemName: model.shiftState.systemName)
.foregroundColor(model.shiftState.foregroundColor)
.frame(width: 32)
.frame(minHeight: 32, maxHeight: .infinity)
.padding(4)
.background(model.shiftState.backgroundColor)
.cornerRadius(8)
.gesture(
DragGesture(minimumDistance: 0.0, coordinateSpace: .global)
.onChanged { _ in
model.touchDown()
}
.onEnded { _ in
model.touchUp()
}
)
}
}
Discussion