Support haptic feedback on new Magic Keyboard
This commit is contained in:
parent
3cb0f46533
commit
5abd265195
|
@ -204,14 +204,19 @@ class FastAccountSwitcherViewController: UIViewController {
|
||||||
@objc private func handleLongPress(_ recognizer: UIGestureRecognizer) {
|
@objc private func handleLongPress(_ recognizer: UIGestureRecognizer) {
|
||||||
switch recognizer.state {
|
switch recognizer.state {
|
||||||
case .began:
|
case .began:
|
||||||
|
show()
|
||||||
|
|
||||||
#if !os(visionOS)
|
#if !os(visionOS)
|
||||||
|
if #available(iOS 17.5, *) {
|
||||||
|
UIImpactFeedbackGenerator(style: .heavy, view: view).impactOccurred(at: CGPoint(x: view.bounds.midX, y: view.bounds.midY))
|
||||||
|
selectionChangedFeedbackGenerator = UISelectionFeedbackGenerator(view: view)
|
||||||
|
} else {
|
||||||
UIImpactFeedbackGenerator(style: .heavy).impactOccurred()
|
UIImpactFeedbackGenerator(style: .heavy).impactOccurred()
|
||||||
selectionChangedFeedbackGenerator = UISelectionFeedbackGenerator()
|
selectionChangedFeedbackGenerator = UISelectionFeedbackGenerator()
|
||||||
|
}
|
||||||
selectionChangedFeedbackGenerator?.prepare()
|
selectionChangedFeedbackGenerator?.prepare()
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
show()
|
|
||||||
|
|
||||||
case .changed:
|
case .changed:
|
||||||
let location = recognizer.location(in: view)
|
let location = recognizer.location(in: view)
|
||||||
|
|
||||||
|
@ -260,7 +265,11 @@ class FastAccountSwitcherViewController: UIViewController {
|
||||||
|
|
||||||
#if !os(visionOS)
|
#if !os(visionOS)
|
||||||
if hapticFeedback {
|
if hapticFeedback {
|
||||||
|
if #available(iOS 17.5, *) {
|
||||||
|
selectionChangedFeedbackGenerator?.selectionChanged(at: location)
|
||||||
|
} else {
|
||||||
selectionChangedFeedbackGenerator?.selectionChanged()
|
selectionChangedFeedbackGenerator?.selectionChanged()
|
||||||
|
}
|
||||||
selectionChangedFeedbackGenerator?.prepare()
|
selectionChangedFeedbackGenerator?.prepare()
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -282,7 +282,11 @@ private class VideoScrubbingControl: UIControl {
|
||||||
sendActions(for: .editingDidBegin)
|
sendActions(for: .editingDidBegin)
|
||||||
|
|
||||||
#if !os(visionOS)
|
#if !os(visionOS)
|
||||||
|
if #available(iOS 17.5, *) {
|
||||||
|
feedbackGenerator = UIImpactFeedbackGenerator(style: .light, view: self)
|
||||||
|
} else {
|
||||||
feedbackGenerator = UIImpactFeedbackGenerator(style: .light)
|
feedbackGenerator = UIImpactFeedbackGenerator(style: .light)
|
||||||
|
}
|
||||||
feedbackGenerator!.prepare()
|
feedbackGenerator!.prepare()
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -301,8 +305,12 @@ private class VideoScrubbingControl: UIControl {
|
||||||
let newFractionComplete = max(0, min(1, unclampedFractionComplete))
|
let newFractionComplete = max(0, min(1, unclampedFractionComplete))
|
||||||
#if !os(visionOS)
|
#if !os(visionOS)
|
||||||
if newFractionComplete != fractionComplete && (newFractionComplete == 0 || newFractionComplete == 1) {
|
if newFractionComplete != fractionComplete && (newFractionComplete == 0 || newFractionComplete == 1) {
|
||||||
|
if #available(iOS 17.5, *) {
|
||||||
|
feedbackGenerator!.impactOccurred(intensity: 0.5, at: location)
|
||||||
|
} else {
|
||||||
feedbackGenerator!.impactOccurred(intensity: 0.5)
|
feedbackGenerator!.impactOccurred(intensity: 0.5)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
fractionComplete = newFractionComplete
|
fractionComplete = newFractionComplete
|
||||||
sendActions(for: .editingChanged)
|
sendActions(for: .editingChanged)
|
||||||
|
|
|
@ -150,7 +150,13 @@ class CustomAlertActionsView: UIControl {
|
||||||
private var separatorSizeConstraints: [NSLayoutConstraint] = []
|
private var separatorSizeConstraints: [NSLayoutConstraint] = []
|
||||||
|
|
||||||
#if !os(visionOS)
|
#if !os(visionOS)
|
||||||
private let generator = UISelectionFeedbackGenerator()
|
private lazy var generator: UISelectionFeedbackGenerator = {
|
||||||
|
if #available(iOS 17.5, *) {
|
||||||
|
UISelectionFeedbackGenerator(view: self)
|
||||||
|
} else {
|
||||||
|
UISelectionFeedbackGenerator()
|
||||||
|
}
|
||||||
|
}()
|
||||||
#endif
|
#endif
|
||||||
private var currentSelectedActionIndex: Int?
|
private var currentSelectedActionIndex: Int?
|
||||||
|
|
||||||
|
@ -313,7 +319,13 @@ class CustomAlertActionsView: UIControl {
|
||||||
actionButtons[currentSelectedActionIndex].backgroundColor = nil
|
actionButtons[currentSelectedActionIndex].backgroundColor = nil
|
||||||
}
|
}
|
||||||
#if !os(visionOS)
|
#if !os(visionOS)
|
||||||
|
if #available(iOS 17.5, *) {
|
||||||
|
let view = selectedButton!.element
|
||||||
|
let location = convert(CGPoint(x: view.bounds.midX, y: view.bounds.midY), from: view)
|
||||||
|
generator.selectionChanged(at: location)
|
||||||
|
} else {
|
||||||
generator.selectionChanged()
|
generator.selectionChanged()
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,13 @@ class PollOptionsView: UIControl {
|
||||||
static let scaledTransform = CGAffineTransform(scaleX: 0.95, y: 0.95)
|
static let scaledTransform = CGAffineTransform(scaleX: 0.95, y: 0.95)
|
||||||
|
|
||||||
#if !os(visionOS)
|
#if !os(visionOS)
|
||||||
private let generator = UISelectionFeedbackGenerator()
|
private lazy var generator: UISelectionFeedbackGenerator = {
|
||||||
|
if #available(iOS 17.5, *) {
|
||||||
|
UISelectionFeedbackGenerator(view: self)
|
||||||
|
} else {
|
||||||
|
UISelectionFeedbackGenerator()
|
||||||
|
}
|
||||||
|
}()
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
override var isEnabled: Bool {
|
override var isEnabled: Bool {
|
||||||
|
@ -159,7 +165,11 @@ class PollOptionsView: UIControl {
|
||||||
animator.startAnimation()
|
animator.startAnimation()
|
||||||
|
|
||||||
#if !os(visionOS)
|
#if !os(visionOS)
|
||||||
|
if #available(iOS 17.5, *) {
|
||||||
|
generator.selectionChanged(at: view.center)
|
||||||
|
} else {
|
||||||
generator.selectionChanged()
|
generator.selectionChanged()
|
||||||
|
}
|
||||||
generator.prepare()
|
generator.prepare()
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -172,7 +182,9 @@ class PollOptionsView: UIControl {
|
||||||
|
|
||||||
override func continueTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
|
override func continueTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
|
||||||
let location = touch.location(in: self)
|
let location = touch.location(in: self)
|
||||||
let newIndex = optionView(at: location)?.1
|
let newIndexAndOption = optionView(at: location)
|
||||||
|
let newIndex = newIndexAndOption?.1
|
||||||
|
let option = newIndexAndOption?.0
|
||||||
|
|
||||||
if newIndex != currentSelectedOptionIndex {
|
if newIndex != currentSelectedOptionIndex {
|
||||||
currentSelectedOptionIndex = newIndex
|
currentSelectedOptionIndex = newIndex
|
||||||
|
@ -189,8 +201,12 @@ class PollOptionsView: UIControl {
|
||||||
animator.startAnimation()
|
animator.startAnimation()
|
||||||
|
|
||||||
#if !os(visionOS)
|
#if !os(visionOS)
|
||||||
if newIndex != nil {
|
if let option {
|
||||||
|
if #available(iOS 17.5, *) {
|
||||||
|
generator.selectionChanged(at: option.center)
|
||||||
|
} else {
|
||||||
generator.selectionChanged()
|
generator.selectionChanged()
|
||||||
|
}
|
||||||
generator.prepare()
|
generator.prepare()
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -24,7 +24,13 @@ class ScrollingSegmentedControl<Value: Hashable>: UIScrollView, UIGestureRecogni
|
||||||
private var changeSelectionPanRecognizer: UIGestureRecognizer!
|
private var changeSelectionPanRecognizer: UIGestureRecognizer!
|
||||||
private var selectedOptionAtStartOfPan: Value?
|
private var selectedOptionAtStartOfPan: Value?
|
||||||
#if !os(visionOS)
|
#if !os(visionOS)
|
||||||
private lazy var selectionChangedFeedbackGenerator = UISelectionFeedbackGenerator()
|
private lazy var selectionChangedFeedbackGenerator: UISelectionFeedbackGenerator = {
|
||||||
|
if #available(iOS 17.5, *) {
|
||||||
|
UISelectionFeedbackGenerator(view: self)
|
||||||
|
} else {
|
||||||
|
UISelectionFeedbackGenerator()
|
||||||
|
}
|
||||||
|
}()
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
override var intrinsicContentSize: CGSize {
|
override var intrinsicContentSize: CGSize {
|
||||||
|
@ -111,14 +117,20 @@ class ScrollingSegmentedControl<Value: Hashable>: UIScrollView, UIGestureRecogni
|
||||||
|
|
||||||
func setSelectedOption(_ value: Value, animated: Bool) {
|
func setSelectedOption(_ value: Value, animated: Bool) {
|
||||||
guard selectedOption != value,
|
guard selectedOption != value,
|
||||||
options.contains(where: { $0.value == value }) else {
|
let index = options.firstIndex(where: { $0.value == value }) else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !os(visionOS)
|
#if !os(visionOS)
|
||||||
if selectedOption != nil {
|
if selectedOption != nil {
|
||||||
|
if #available(iOS 17.5, *) {
|
||||||
|
let optionView = optionsStack.arrangedSubviews[index]
|
||||||
|
let location = convert(CGPoint(x: optionView.bounds.midX, y: optionView.bounds.midY), from: optionView)
|
||||||
|
selectionChangedFeedbackGenerator.selectionChanged(at: location)
|
||||||
|
} else {
|
||||||
selectionChangedFeedbackGenerator.selectionChanged()
|
selectionChangedFeedbackGenerator.selectionChanged()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
selectedOption = value
|
selectedOption = value
|
||||||
|
@ -158,15 +170,19 @@ class ScrollingSegmentedControl<Value: Hashable>: UIScrollView, UIGestureRecogni
|
||||||
// MARK: Interaction
|
// MARK: Interaction
|
||||||
|
|
||||||
override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
|
override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||||
|
guard gestureRecognizer === self.panGestureRecognizer else {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
let beganOnSelectedOption: Bool
|
let beganOnSelectedOption: Bool
|
||||||
if let selectedIndex = options.firstIndex(where: { $0.value == selectedOption }),
|
if let selectedIndex = options.firstIndex(where: { $0.value == selectedOption }),
|
||||||
optionsStack.arrangedSubviews[selectedIndex].frame.contains(self.panGestureRecognizer.location(in: optionsStack)) {
|
optionsStack.arrangedSubviews[selectedIndex].frame.contains(gestureRecognizer.location(in: optionsStack)) {
|
||||||
beganOnSelectedOption = true
|
beganOnSelectedOption = true
|
||||||
} else {
|
} else {
|
||||||
beganOnSelectedOption = false
|
beganOnSelectedOption = false
|
||||||
}
|
}
|
||||||
|
|
||||||
// only begin changing selection if the gesutre started on the currently selected item
|
// only begin changing selection if the gesture started on the currently selected item
|
||||||
// otherwise, let the scroll view handle things
|
// otherwise, let the scroll view handle things
|
||||||
if gestureRecognizer == self.changeSelectionPanRecognizer {
|
if gestureRecognizer == self.changeSelectionPanRecognizer {
|
||||||
return beganOnSelectedOption
|
return beganOnSelectedOption
|
||||||
|
@ -223,7 +239,12 @@ class ScrollingSegmentedControl<Value: Hashable>: UIScrollView, UIGestureRecogni
|
||||||
}
|
}
|
||||||
animator.startAnimation()
|
animator.startAnimation()
|
||||||
#if !os(visionOS)
|
#if !os(visionOS)
|
||||||
|
if #available(iOS 17.5, *) {
|
||||||
|
let locationInSelf = convert(location, from: optionsStack)
|
||||||
|
selectionChangedFeedbackGenerator.selectionChanged(at: locationInSelf)
|
||||||
|
} else {
|
||||||
selectionChangedFeedbackGenerator.selectionChanged()
|
selectionChangedFeedbackGenerator.selectionChanged()
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Reference in New Issue