From b983a4336b45465c4d8df9f471edd185cbbbe12a Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Tue, 4 Oct 2022 16:35:28 +0800 Subject: [PATCH] Dont try to highlight beyond the last item despite speech marks offset data --- .../AudioPlayer/ScrollingStackModifier.swift | 78 +++++++++++++++++++ .../AudioSession/AudioController.swift | 15 +++- 2 files changed, 91 insertions(+), 2 deletions(-) create mode 100644 apple/OmnivoreKit/Sources/App/Views/AudioPlayer/ScrollingStackModifier.swift diff --git a/apple/OmnivoreKit/Sources/App/Views/AudioPlayer/ScrollingStackModifier.swift b/apple/OmnivoreKit/Sources/App/Views/AudioPlayer/ScrollingStackModifier.swift new file mode 100644 index 000000000..1bce64649 --- /dev/null +++ b/apple/OmnivoreKit/Sources/App/Views/AudioPlayer/ScrollingStackModifier.swift @@ -0,0 +1,78 @@ +// +// ScrollingStackModifier.swift +// ScrollView_Tests +// +// Created by Jean-Marc Boullianne on 8/7/20. +// +import SwiftUI + +struct ScrollingHStackModifier: ViewModifier { + + @State private var scrollOffset: CGFloat + @State private var dragOffset: CGFloat + + var items: Int + var itemWidth: CGFloat + var itemSpacing: CGFloat + + init(items: Int, itemWidth: CGFloat, itemSpacing: CGFloat) { + self.items = items + self.itemWidth = itemWidth + self.itemSpacing = itemSpacing + + // Calculate Total Content Width + let contentWidth: CGFloat = CGFloat(items) * itemWidth + CGFloat(items - 1) * itemSpacing + let screenWidth = UIScreen.main.bounds.width + + // Set Initial Offset to first Item + let initialOffset = (contentWidth/2.0) - (screenWidth/2.0) + ((screenWidth - itemWidth) / 2.0) + + self._scrollOffset = State(initialValue: initialOffset) + self._dragOffset = State(initialValue: 0) + } + + func body(content: Content) -> some View { + content + .offset(x: scrollOffset + dragOffset, y: 0) + .gesture(DragGesture() + .onChanged({ event in + dragOffset = event.translation.width + }) + .onEnded({ event in + // Scroll to where user dragged + scrollOffset += event.translation.width + dragOffset = 0 + + // Now calculate which item to snap to + let contentWidth: CGFloat = CGFloat(items) * itemWidth + CGFloat(items - 1) * itemSpacing + let screenWidth = UIScreen.main.bounds.width + + // Center position of current offset + let center = scrollOffset + (screenWidth / 2.0) + (contentWidth / 2.0) + + // Calculate which item we are closest to using the defined size + var index = (center - (screenWidth / 2.0)) / (itemWidth + itemSpacing) + + // Should we stay at current index or are we closer to the next item... + if index.remainder(dividingBy: 1) > 0.5 { + index += 1 + } else { + index = CGFloat(Int(index)) + } + + // Protect from scrolling out of bounds + index = min(index, CGFloat(items) - 1) + index = max(index, 0) + + // Set final offset (snapping to item) + let newOffset = index * itemWidth + (index - 1) * itemSpacing - (contentWidth / 2.0) + (screenWidth / 2.0) - ((screenWidth - itemWidth) / 2.0) + itemSpacing + + // Animate snapping + withAnimation { + scrollOffset = newOffset + } + + }) + ) + } +} diff --git a/apple/OmnivoreKit/Sources/Services/AudioSession/AudioController.swift b/apple/OmnivoreKit/Sources/Services/AudioSession/AudioController.swift index e2a10681e..75b82a078 100644 --- a/apple/OmnivoreKit/Sources/Services/AudioSession/AudioController.swift +++ b/apple/OmnivoreKit/Sources/Services/AudioSession/AudioController.swift @@ -198,6 +198,7 @@ public class AudioController: NSObject, ObservableObject, AVAudioPlayerDelegate @Published public var currentAudioIndex: Int = 0 @Published public var readText: String = "" @Published public var unreadText: String = "" + @Published public var numberOfSpeechItems: Int = 0 @Published public var itemAudioProperties: LinkedItemAudioProperties? @@ -254,6 +255,8 @@ public class AudioController: NSObject, ObservableObject, AVAudioPlayerDelegate player?.removeAllItems() document = nil + numberOfSpeechItems = 0 + timer = nil player = nil observer = nil @@ -476,7 +479,13 @@ public class AudioController: NSObject, ObservableObject, AVAudioPlayerDelegate public var textItems: [String]? { if let document = self.document { - return document.utterances.map(\.text) + return document.utterances.map { utterance in + if let regex = try? NSRegularExpression(pattern: "<[^>]*>", options: .caseInsensitive) { + let modString = regex.stringByReplacingMatches(in: utterance.text, options: [], range: NSRange(location: 0, length: utterance.text.count), withTemplate: "") + return modString + } + return "" + } } return nil } @@ -503,7 +512,7 @@ public class AudioController: NSObject, ObservableObject, AVAudioPlayerDelegate let idx = item.speechItem.audioIdx let currentItem = textItems[idx] - let currentReadIndex = currentItem.index(currentItem.startIndex, offsetBy: currentItemOffset) + let currentReadIndex = currentItem.index(currentItem.startIndex, offsetBy: max(currentItemOffset, currentItem.count)) let lastItem = String(currentItem[..