diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index ee1a2519..402008c2 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -6,7 +6,7 @@ on: types: [closed] env: DEVELOPER_DIR: /Applications/Xcode_13.0.app - APP_VERSION: '1.0.6' + APP_VERSION: '1.1.0' SCHEME_NAME: 'EhPanda' BUILDS_PATH: '/tmp/action-builds' ARCHIVE_PATH: '/tmp/action-builds/EhPanda.xcarchive' diff --git a/.swiftlint.yml b/.swiftlint.yml index 5abbcd7c..22adb80e 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -8,6 +8,8 @@ disabled_rules: identifier_name: excluded: + - x + - y - id - no diff --git a/EhPanda/App/EhPandaApp.swift b/EhPanda/App/EhPandaApp.swift index 2d476fed..4b5b2f7a 100644 --- a/EhPanda/App/EhPandaApp.swift +++ b/EhPanda/App/EhPandaApp.swift @@ -45,6 +45,7 @@ private extension EhPandaApp { configureWebImage() configureDomainFronting() } + addTouchHandler() configureLogging() fetchTagTranslator() fetchIgneousIfNeeded() @@ -87,6 +88,15 @@ private extension EhPandaApp { // MARK: Configuration private extension EhPandaApp { + func addTouchHandler() { + DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) { + let tapGesture = UITapGestureRecognizer( + target: self, action: nil + ) + tapGesture.delegate = TouchHandler.shared + keyWindow?.addGestureRecognizer(tapGesture) + } + } func configureLogging() { var file = FileDestination() var console = ConsoleDestination() @@ -153,3 +163,16 @@ class AppDelegate: UIResponder, UIApplicationDelegate { supportedInterfaceOrientationsFor window: UIWindow? ) -> UIInterfaceOrientationMask { AppDelegate.orientationLock } } + +final class TouchHandler: NSObject, UIGestureRecognizerDelegate { + static let shared = TouchHandler() + var currentPoint: CGPoint? + + func gestureRecognizer( + _ gestureRecognizer: UIGestureRecognizer, + shouldReceive touch: UITouch + ) -> Bool { + currentPoint = touch.location(in: touch.window) + return false + } +} diff --git a/EhPanda/App/Extensions.swift b/EhPanda/App/Extensions.swift index b9434cda..0da7450d 100644 --- a/EhPanda/App/Extensions.swift +++ b/EhPanda/App/Extensions.swift @@ -21,6 +21,7 @@ extension Dictionary where Key == String, Value == String { } } +// Enables fullscreen swipe back gesture extension UINavigationController: UIGestureRecognizerDelegate { override open func viewDidLoad() { super.viewDidLoad() diff --git a/EhPanda/View/Reading/ReadingView.swift b/EhPanda/View/Reading/ReadingView.swift index 74e3b1a6..791fc1a2 100644 --- a/EhPanda/View/Reading/ReadingView.swift +++ b/EhPanda/View/Reading/ReadingView.swift @@ -28,6 +28,7 @@ struct ReadingView: View, StoreAccessor, PersistenceAccessor { @State private var sliderValue: Float = 1 @State private var sheetState: ReadingViewSheetState? + @State private var scaleAnchor: UnitPoint = .center @State private var scale: CGFloat = 1 @State private var baseScale: CGFloat = 1 @State private var offset: CGSize = .zero @@ -193,12 +194,10 @@ struct ReadingView: View, StoreAccessor, PersistenceAccessor { ZStack { backgroundColor.ignoresSafeArea() conditionalList - .scaleEffect(scale).offset(offset) - .transition(opacityTransition) - .gesture(tapGesture) - .gesture(dragGesture) - .gesture(magnifyGesture) - .ignoresSafeArea() + .scaleEffect(scale, anchor: scaleAnchor) + .offset(offset).transition(opacityTransition) + .gesture(tapGesture).gesture(dragGesture) + .gesture(magnifyGesture).ignoresSafeArea() ControlPanel( showsPanel: $showsPanel, sliderValue: $sliderValue, @@ -550,6 +549,7 @@ private extension ReadingView { } } func onDoubleTap(_: TapGesture.Value) { + syncScaleAnchor() set(newOffset: .zero) set(newScale: scale == 1 ? setting.doubleTapScaleFactor : 1) } @@ -557,12 +557,9 @@ private extension ReadingView { func onDragGestureChanged(value: DragGesture.Value) { if scale > 1 { let newX = value.translation.width + newOffset.width - let marginW = windowW * (scale - 1) / 2 - let newOffsetW = min(max(newX, -marginW), marginW) - let newY = value.translation.height + newOffset.height - let marginH = windowH * (scale - 1) / 2 - let newOffsetH = min(max(newY, -marginH), marginH) + let newOffsetW = fixWidth(x: newX) + let newOffsetH = fixHeight(y: newY) set(newOffset: CGSize(width: newOffsetW, height: newOffsetH)) } @@ -579,6 +576,7 @@ private extension ReadingView { if value == 1 { baseScale = scale } + syncScaleAnchor() set(newScale: value * baseScale) } func onMagnificationGestureEnded(value: MagnificationGesture.Value) { @@ -609,17 +607,31 @@ private extension ReadingView { } fixOffset() } - func fixOffset() { - let marginW = windowW * (scale - 1) / 2 - let marginH = windowH * (scale - 1) / 2 - let currentW = offset.width - let currentH = offset.height + func syncScaleAnchor() { + guard let point = TouchHandler.shared.currentPoint else { return } + let x = min(max(point.x / absWindowW, 0), 1) + let y = min(max(point.y / absWindowH, 0), 1) + scaleAnchor = UnitPoint(x: x, y: y) + } + func fixOffset() { withAnimation { - offset.width = min(max(currentW, -marginW), marginW) - offset.height = min(max(currentH, -marginH), marginH) + offset.width = fixWidth(x: offset.width) + offset.height = fixHeight(y: offset.height) } } + func fixWidth(x: CGFloat) -> CGFloat { + let marginW = absWindowW * (scale - 1) / 2 + let leadingMargin = scaleAnchor.x / 0.5 * marginW + let trailingMargin = (1 - scaleAnchor.x) / 0.5 * marginW + return min(max(x, -trailingMargin), leadingMargin) + } + func fixHeight(y: CGFloat) -> CGFloat { + let marginH = absWindowH * (scale - 1) / 2 + let topMargin = scaleAnchor.y / 0.5 * marginH + let bottomMargin = (1 - scaleAnchor.y) / 0.5 * marginH + return min(max(y, -bottomMargin), topMargin) + } } // MARK: ImageContainer