From 775a63eab289baf9ff7545f6ab5f42c6cceef4f6 Mon Sep 17 00:00:00 2001 From: Mohamed Afifi Date: Sun, 4 Aug 2024 15:40:10 -0400 Subject: [PATCH] Scroll to verse when orientation changes (#643) --- .../QuranImageFeature/ContentImageView.swift | 48 +++++++++---------- .../ContentImageViewModel.swift | 8 ++-- .../ContentTranslationView.swift | 28 ++++------- .../QuranGeometry}/ImagePage.swift | 7 ++- .../Quran/QuranScrollingViewModifier.swift | 45 +++++++++++++++++ 5 files changed, 87 insertions(+), 49 deletions(-) rename {Domain/ImageService/Sources => Model/QuranGeometry}/ImagePage.swift (61%) create mode 100644 UI/NoorUI/Features/Quran/QuranScrollingViewModifier.swift diff --git a/Features/QuranImageFeature/ContentImageView.swift b/Features/QuranImageFeature/ContentImageView.swift index 5296070e..0dbb2c8c 100644 --- a/Features/QuranImageFeature/ContentImageView.swift +++ b/Features/QuranImageFeature/ContentImageView.swift @@ -7,6 +7,7 @@ import NoorUI import QuranGeometry +import QuranKit import SwiftUI struct ContentImageView: View { @@ -20,7 +21,8 @@ struct ContentImageView: View { quarterName: viewModel.page.localizedQuarterName, suraNames: viewModel.page.suraNames(), page: viewModel.page.localizedNumber, - scrollToItem: viewModel.scrollToItem, + scrollToVerse: viewModel.scrollToVerse, + wordFrames: viewModel.imagePage?.wordFrames, onScaleChange: { viewModel.scale = $0 }, onGlobalFrameChange: { viewModel.imageFrame = $0 } ) @@ -37,33 +39,28 @@ private struct ContentImageViewBody: View { let quarterName: String let suraNames: MultipartText let page: String - let scrollToItem: WordFrameLine? + let scrollToVerse: AyahNumber? + let wordFrames: WordFrameCollection? let onScaleChange: (WordFrameScale) -> Void let onGlobalFrameChange: (CGRect) -> Void var body: some View { - ScrollViewReader { scrollView in - AdaptiveImageScrollView(decorations: decorations) { - image - } onScaleChange: { - onScaleChange($0) - } onGlobalFrameChange: { - onGlobalFrameChange($0) - } header: { - QuranPageHeader(quarterName: quarterName, suraNames: suraNames) - } footer: { - QuranPageFooter(page: page) - } - // TODO: Should be part of the headers and footers. - .font(.footnote) - .populateReadableInsets() - .onChange(of: scrollToItem) { scrollToItem in - if let scrollToItem { - withAnimation { - scrollView.scrollTo(scrollToItem, anchor: UnitPoint(x: 0, y: 0.2)) - } - } - } + AdaptiveImageScrollView(decorations: decorations) { + image + } onScaleChange: { + onScaleChange($0) + } onGlobalFrameChange: { + onGlobalFrameChange($0) + } header: { + QuranPageHeader(quarterName: quarterName, suraNames: suraNames) + } footer: { + QuranPageFooter(page: page) + } + // TODO: Should be part of the headers and footers. + .font(.footnote) + .populateReadableInsets() + .quranScrolling(scrollToValue: scrollToVerse) { + wordFrames?.lineFramesVerVerse($0).first } } } @@ -80,7 +77,8 @@ private struct ContentImageViewBody: View { quarterName: "ABC", suraNames: "ABC", page: "604", - scrollToItem: nil, + scrollToVerse: nil, + wordFrames: nil, onScaleChange: { _ in }, onGlobalFrameChange: { _ in } ) diff --git a/Features/QuranImageFeature/ContentImageViewModel.swift b/Features/QuranImageFeature/ContentImageViewModel.swift index 52e1bc5c..0d9a0b12 100644 --- a/Features/QuranImageFeature/ContentImageViewModel.swift +++ b/Features/QuranImageFeature/ContentImageViewModel.swift @@ -45,7 +45,7 @@ class ContentImageViewModel: ObservableObject { @Published var suraHeaderLocations: [SuraHeaderLocation] = [] @Published var ayahNumberLocations: [AyahNumberLocation] = [] @Published var highlights: QuranHighlights - @Published var scrollToItem: WordFrameLine? + @Published var scrollToVerse: AyahNumber? @Published var scale: WordFrameScale = .zero @Published var imageFrame: CGRect = .zero @@ -108,10 +108,8 @@ class ContentImageViewModel: ObservableObject { guard let ayah = highlightsService.highlights.firstScrollingVerse() else { return } - if let line = imagePage?.wordFrames.lineFramesVerVerse(ayah).first { - logger.info("Quran Image: scrollToVerseIfNeeded \(ayah) - \(line.frames)") - scrollToItem = line - } + logger.info("Quran Image: scrollToVerseIfNeeded \(ayah)") + scrollToVerse = ayah } private func scrollToVerseIfNeeded() { diff --git a/Features/QuranTranslationFeature/ContentTranslationView.swift b/Features/QuranTranslationFeature/ContentTranslationView.swift index 219b2bc0..e297cce2 100644 --- a/Features/QuranTranslationFeature/ContentTranslationView.swift +++ b/Features/QuranTranslationFeature/ContentTranslationView.swift @@ -50,25 +50,17 @@ private struct ContentTranslationViewBody: View { let openURL: (TranslationURL) -> Void var body: some View { - ScrollViewReader { scrollView in - List { - ForEach(items) { item in - item - } - } - .listStyle(.plain) - .environment(\.defaultMinListRowHeight, 1) - .populateReadableInsets() - .openTranslationURL(openURL) - .trackCollection(with: tracker) - .sheet(item: $footnote) { $0 } - .onChange(of: scrollToItem) { scrollToItem in - if let scrollToItem { - withAnimation { - scrollView.scrollTo(scrollToItem, anchor: UnitPoint(x: 0.2, y: 0.2)) - } - } + List { + ForEach(items) { item in + item } } + .listStyle(.plain) + .environment(\.defaultMinListRowHeight, 1) + .populateReadableInsets() + .openTranslationURL(openURL) + .trackCollection(with: tracker) + .sheet(item: $footnote) { $0 } + .quranScrolling(scrollToValue: scrollToItem) } } diff --git a/Domain/ImageService/Sources/ImagePage.swift b/Model/QuranGeometry/ImagePage.swift similarity index 61% rename from Domain/ImageService/Sources/ImagePage.swift rename to Model/QuranGeometry/ImagePage.swift index 9f67da83..15e0e637 100644 --- a/Domain/ImageService/Sources/ImagePage.swift +++ b/Model/QuranGeometry/ImagePage.swift @@ -6,7 +6,6 @@ // Copyright © 2019 Quran.com. All rights reserved. // -import QuranGeometry import QuranKit import UIKit @@ -14,4 +13,10 @@ public struct ImagePage: Equatable { public let image: UIImage public let wordFrames: WordFrameCollection public let startAyah: AyahNumber + + public init(image: UIImage, wordFrames: WordFrameCollection, startAyah: AyahNumber) { + self.image = image + self.wordFrames = wordFrames + self.startAyah = startAyah + } } diff --git a/UI/NoorUI/Features/Quran/QuranScrollingViewModifier.swift b/UI/NoorUI/Features/Quran/QuranScrollingViewModifier.swift new file mode 100644 index 00000000..14a72749 --- /dev/null +++ b/UI/NoorUI/Features/Quran/QuranScrollingViewModifier.swift @@ -0,0 +1,45 @@ +// +// QuranScrollingViewModifier.swift +// +// +// Created by Mohamed Afifi on 2024-08-03. +// + +import SwiftUI + +struct QuranScrollingViewModifier: ViewModifier { + let scrollToValue: Value? + let transform: (Value) -> ID? + @State private var scrollToValueRequest: Value? + + func body(content: Content) -> some View { + ScrollViewReader { scrollView in + content + .onChange(of: scrollToValue) { scrollToValueRequest = $0 } + .onChange(of: scrollToValueRequest) { scrollToValue in + if let scrollToValue, let id = transform(scrollToValue) { + withAnimation { + scrollView.scrollTo(id, anchor: UnitPoint(x: 0, y: 0.2)) + } + scrollToValueRequest = nil + } + } + } + .onSizeChange { _ in + scrollToValueRequest = scrollToValue + } + } +} + +extension View { + public func quranScrolling( + scrollToValue: Value?, + transform: @escaping (Value) -> (some Hashable)? + ) -> some View { + modifier(QuranScrollingViewModifier(scrollToValue: scrollToValue, transform: transform)) + } + + public func quranScrolling(scrollToValue: (some Hashable)?) -> some View { + modifier(QuranScrollingViewModifier(scrollToValue: scrollToValue) { $0 }) + } +}