Support haptic feedback on new Magic Keyboard

This commit is contained in:
Shadowfacts 2024-07-06 17:10:29 -07:00
parent 3cb0f46533
commit 5abd265195
5 changed files with 87 additions and 21 deletions

View File

@ -204,14 +204,19 @@ class FastAccountSwitcherViewController: UIViewController {
@objc private func handleLongPress(_ recognizer: UIGestureRecognizer) {
switch recognizer.state {
case .began:
show()
#if !os(visionOS)
UIImpactFeedbackGenerator(style: .heavy).impactOccurred()
selectionChangedFeedbackGenerator = UISelectionFeedbackGenerator()
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()
selectionChangedFeedbackGenerator = UISelectionFeedbackGenerator()
}
selectionChangedFeedbackGenerator?.prepare()
#endif
show()
case .changed:
let location = recognizer.location(in: view)
@ -260,7 +265,11 @@ class FastAccountSwitcherViewController: UIViewController {
#if !os(visionOS)
if hapticFeedback {
selectionChangedFeedbackGenerator?.selectionChanged()
if #available(iOS 17.5, *) {
selectionChangedFeedbackGenerator?.selectionChanged(at: location)
} else {
selectionChangedFeedbackGenerator?.selectionChanged()
}
selectionChangedFeedbackGenerator?.prepare()
}
#endif

View File

@ -282,7 +282,11 @@ private class VideoScrubbingControl: UIControl {
sendActions(for: .editingDidBegin)
#if !os(visionOS)
feedbackGenerator = UIImpactFeedbackGenerator(style: .light)
if #available(iOS 17.5, *) {
feedbackGenerator = UIImpactFeedbackGenerator(style: .light, view: self)
} else {
feedbackGenerator = UIImpactFeedbackGenerator(style: .light)
}
feedbackGenerator!.prepare()
#endif
@ -301,7 +305,11 @@ private class VideoScrubbingControl: UIControl {
let newFractionComplete = max(0, min(1, unclampedFractionComplete))
#if !os(visionOS)
if newFractionComplete != fractionComplete && (newFractionComplete == 0 || newFractionComplete == 1) {
feedbackGenerator!.impactOccurred(intensity: 0.5)
if #available(iOS 17.5, *) {
feedbackGenerator!.impactOccurred(intensity: 0.5, at: location)
} else {
feedbackGenerator!.impactOccurred(intensity: 0.5)
}
}
#endif
fractionComplete = newFractionComplete

View File

@ -150,7 +150,13 @@ class CustomAlertActionsView: UIControl {
private var separatorSizeConstraints: [NSLayoutConstraint] = []
#if !os(visionOS)
private let generator = UISelectionFeedbackGenerator()
private lazy var generator: UISelectionFeedbackGenerator = {
if #available(iOS 17.5, *) {
UISelectionFeedbackGenerator(view: self)
} else {
UISelectionFeedbackGenerator()
}
}()
#endif
private var currentSelectedActionIndex: Int?
@ -313,7 +319,13 @@ class CustomAlertActionsView: UIControl {
actionButtons[currentSelectedActionIndex].backgroundColor = nil
}
#if !os(visionOS)
generator.selectionChanged()
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()
}
#endif
}

View File

@ -30,7 +30,13 @@ class PollOptionsView: UIControl {
static let scaledTransform = CGAffineTransform(scaleX: 0.95, y: 0.95)
#if !os(visionOS)
private let generator = UISelectionFeedbackGenerator()
private lazy var generator: UISelectionFeedbackGenerator = {
if #available(iOS 17.5, *) {
UISelectionFeedbackGenerator(view: self)
} else {
UISelectionFeedbackGenerator()
}
}()
#endif
override var isEnabled: Bool {
@ -159,7 +165,11 @@ class PollOptionsView: UIControl {
animator.startAnimation()
#if !os(visionOS)
generator.selectionChanged()
if #available(iOS 17.5, *) {
generator.selectionChanged(at: view.center)
} else {
generator.selectionChanged()
}
generator.prepare()
#endif
@ -172,8 +182,10 @@ class PollOptionsView: UIControl {
override func continueTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
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 {
currentSelectedOptionIndex = newIndex
@ -189,8 +201,12 @@ class PollOptionsView: UIControl {
animator.startAnimation()
#if !os(visionOS)
if newIndex != nil {
generator.selectionChanged()
if let option {
if #available(iOS 17.5, *) {
generator.selectionChanged(at: option.center)
} else {
generator.selectionChanged()
}
generator.prepare()
}
#endif

View File

@ -24,7 +24,13 @@ class ScrollingSegmentedControl<Value: Hashable>: UIScrollView, UIGestureRecogni
private var changeSelectionPanRecognizer: UIGestureRecognizer!
private var selectedOptionAtStartOfPan: Value?
#if !os(visionOS)
private lazy var selectionChangedFeedbackGenerator = UISelectionFeedbackGenerator()
private lazy var selectionChangedFeedbackGenerator: UISelectionFeedbackGenerator = {
if #available(iOS 17.5, *) {
UISelectionFeedbackGenerator(view: self)
} else {
UISelectionFeedbackGenerator()
}
}()
#endif
override var intrinsicContentSize: CGSize {
@ -111,13 +117,19 @@ class ScrollingSegmentedControl<Value: Hashable>: UIScrollView, UIGestureRecogni
func setSelectedOption(_ value: Value, animated: Bool) {
guard selectedOption != value,
options.contains(where: { $0.value == value }) else {
let index = options.firstIndex(where: { $0.value == value }) else {
return
}
#if !os(visionOS)
if selectedOption != nil {
selectionChangedFeedbackGenerator.selectionChanged()
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()
}
}
#endif
@ -158,15 +170,19 @@ class ScrollingSegmentedControl<Value: Hashable>: UIScrollView, UIGestureRecogni
// MARK: Interaction
override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
guard gestureRecognizer === self.panGestureRecognizer else {
return true
}
let beganOnSelectedOption: Bool
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
} else {
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
if gestureRecognizer == self.changeSelectionPanRecognizer {
return beganOnSelectedOption
@ -223,7 +239,12 @@ class ScrollingSegmentedControl<Value: Hashable>: UIScrollView, UIGestureRecogni
}
animator.startAnimation()
#if !os(visionOS)
selectionChangedFeedbackGenerator.selectionChanged()
if #available(iOS 17.5, *) {
let locationInSelf = convert(location, from: optionsStack)
selectionChangedFeedbackGenerator.selectionChanged(at: locationInSelf)
} else {
selectionChangedFeedbackGenerator.selectionChanged()
}
#endif
return true
} else {