Animate the transition to full screen image view

This commit is contained in:
Maurice Parker 2019-10-15 18:08:13 -05:00
parent 6ae36303de
commit 425b3b09a1
5 changed files with 110 additions and 6 deletions

View File

@ -103,6 +103,7 @@
518651B023555EB20078E021 /* NNW3FeedsImporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 518651A923555EB20078E021 /* NNW3FeedsImporter.swift */; }; 518651B023555EB20078E021 /* NNW3FeedsImporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 518651A923555EB20078E021 /* NNW3FeedsImporter.swift */; };
518651B123555EB20078E021 /* NNW3Feed.swift in Sources */ = {isa = PBXBuildFile; fileRef = 518651AA23555EB20078E021 /* NNW3Feed.swift */; }; 518651B123555EB20078E021 /* NNW3Feed.swift in Sources */ = {isa = PBXBuildFile; fileRef = 518651AA23555EB20078E021 /* NNW3Feed.swift */; };
518651B223555EB20078E021 /* NNW3Document.swift in Sources */ = {isa = PBXBuildFile; fileRef = 518651AB23555EB20078E021 /* NNW3Document.swift */; }; 518651B223555EB20078E021 /* NNW3Document.swift in Sources */ = {isa = PBXBuildFile; fileRef = 518651AB23555EB20078E021 /* NNW3Document.swift */; };
518651DA235621840078E021 /* ImageTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 518651D9235621840078E021 /* ImageTransition.swift */; };
518B2EE82351B45600400001 /* NetNewsWire_iOSTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 840D61952029031D009BC708 /* NetNewsWire_iOSTests.swift */; }; 518B2EE82351B45600400001 /* NetNewsWire_iOSTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 840D61952029031D009BC708 /* NetNewsWire_iOSTests.swift */; };
51934CCB230F599B006127BE /* ThemedNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51934CC1230F5963006127BE /* ThemedNavigationController.swift */; }; 51934CCB230F599B006127BE /* ThemedNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51934CC1230F5963006127BE /* ThemedNavigationController.swift */; };
51934CCE2310792F006127BE /* ActivityManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51934CCD2310792F006127BE /* ActivityManager.swift */; }; 51934CCE2310792F006127BE /* ActivityManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51934CCD2310792F006127BE /* ActivityManager.swift */; };
@ -798,6 +799,7 @@
518651A923555EB20078E021 /* NNW3FeedsImporter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NNW3FeedsImporter.swift; sourceTree = "<group>"; }; 518651A923555EB20078E021 /* NNW3FeedsImporter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NNW3FeedsImporter.swift; sourceTree = "<group>"; };
518651AA23555EB20078E021 /* NNW3Feed.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NNW3Feed.swift; sourceTree = "<group>"; }; 518651AA23555EB20078E021 /* NNW3Feed.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NNW3Feed.swift; sourceTree = "<group>"; };
518651AB23555EB20078E021 /* NNW3Document.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NNW3Document.swift; sourceTree = "<group>"; }; 518651AB23555EB20078E021 /* NNW3Document.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NNW3Document.swift; sourceTree = "<group>"; };
518651D9235621840078E021 /* ImageTransition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageTransition.swift; sourceTree = "<group>"; };
518B2ED22351B3DD00400001 /* NetNewsWire-iOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "NetNewsWire-iOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 518B2ED22351B3DD00400001 /* NetNewsWire-iOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "NetNewsWire-iOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
518B2EE92351B4C200400001 /* NetNewsWire_iOSTests_target.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = NetNewsWire_iOSTests_target.xcconfig; sourceTree = "<group>"; }; 518B2EE92351B4C200400001 /* NetNewsWire_iOSTests_target.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = NetNewsWire_iOSTests_target.xcconfig; sourceTree = "<group>"; };
51934CC1230F5963006127BE /* ThemedNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemedNavigationController.swift; sourceTree = "<group>"; }; 51934CC1230F5963006127BE /* ThemedNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemedNavigationController.swift; sourceTree = "<group>"; };
@ -1397,6 +1399,7 @@
51102164233A7D6C0007A5F7 /* ArticleExtractorButton.swift */, 51102164233A7D6C0007A5F7 /* ArticleExtractorButton.swift */,
5142192923522B5500E07E2C /* ImageViewController.swift */, 5142192923522B5500E07E2C /* ImageViewController.swift */,
514219362352510100E07E2C /* ImageScrollView.swift */, 514219362352510100E07E2C /* ImageScrollView.swift */,
518651D9235621840078E021 /* ImageTransition.swift */,
); );
path = Article; path = Article;
sourceTree = "<group>"; sourceTree = "<group>";
@ -2919,6 +2922,7 @@
5183CCE9226F68D90010922C /* AccountRefreshTimer.swift in Sources */, 5183CCE9226F68D90010922C /* AccountRefreshTimer.swift in Sources */,
51C452882265093600C03939 /* AddFeedViewController.swift in Sources */, 51C452882265093600C03939 /* AddFeedViewController.swift in Sources */,
51934CCE2310792F006127BE /* ActivityManager.swift in Sources */, 51934CCE2310792F006127BE /* ActivityManager.swift in Sources */,
518651DA235621840078E021 /* ImageTransition.swift in Sources */,
514219372352510100E07E2C /* ImageScrollView.swift in Sources */, 514219372352510100E07E2C /* ImageScrollView.swift in Sources */,
DF999FF722B5AEFA0064B687 /* SafariView.swift in Sources */, DF999FF722B5AEFA0064B687 /* SafariView.swift in Sources */,
51C4529B22650A1000C03939 /* FaviconDownloader.swift in Sources */, 51C4529B22650A1000C03939 /* FaviconDownloader.swift in Sources */,

View File

@ -43,6 +43,7 @@ class ArticleViewController: UIViewController {
}() }()
private var webView: WKWebView! private var webView: WKWebView!
private var transition = ImageTransition()
weak var coordinator: SceneCoordinator! weak var coordinator: SceneCoordinator!
@ -66,6 +67,9 @@ class ArticleViewController: UIViewController {
} }
} }
var clickedImage: UIImage?
var clickedImageFrame: CGRect?
var articleExtractorButtonState: ArticleExtractorButtonState { var articleExtractorButtonState: ArticleExtractorButtonState {
get { get {
return articleExtractorButton.buttonState return articleExtractorButton.buttonState
@ -357,17 +361,43 @@ extension ArticleViewController: WKScriptMessageHandler {
let base64Image = String(clickMessage.imageURL.suffix(from: range.upperBound)) let base64Image = String(clickMessage.imageURL.suffix(from: range.upperBound))
if let imageData = Data(base64Encoded: base64Image), let image = UIImage(data: imageData) { if let imageData = Data(base64Encoded: base64Image), let image = UIImage(data: imageData) {
let rect = CGRect(x: CGFloat(clickMessage.x), y: CGFloat(clickMessage.y), width: CGFloat(clickMessage.width), height: CGFloat(clickMessage.height))
clickedImageFrame = webView.convert(rect, to: nil)
clickedImage = image
let imageVC = UIStoryboard.main.instantiateController(ofType: ImageViewController.self) let imageVC = UIStoryboard.main.instantiateController(ofType: ImageViewController.self)
imageVC.image = image imageVC.image = image
imageVC.modalPresentationStyle = .fullScreen imageVC.modalPresentationStyle = .fullScreen
imageVC.transitioningDelegate = self
present(imageVC, animated: true) present(imageVC, animated: true)
} }
} }
} }
} }
// MARK: UIViewControllerTransitioningDelegate
extension ArticleViewController: UIViewControllerTransitioningDelegate {
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
guard let frame = clickedImageFrame, let image = clickedImage else { return nil }
transition.originFrame = frame
transition.originImage = image
transition.presenting = true
return transition
}
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
transition.presenting = false
return transition
}
}
// MARK: JSON // MARK: JSON
private struct TemplateData: Codable { private struct TemplateData: Codable {
let style: String let style: String
let body: String let body: String

View File

@ -42,6 +42,10 @@ open class ImageScrollView: UIScrollView {
private var scaleToRestoreAfterResize: CGFloat = 1.0 private var scaleToRestoreAfterResize: CGFloat = 1.0
var maxScaleFromMinScale: CGFloat = 3.0 var maxScaleFromMinScale: CGFloat = 3.0
var zoomedFrame: CGRect {
return convert(zoomView?.frame ?? CGRect.zero, to: nil)
}
override open var frame: CGRect { override open var frame: CGRect {
willSet { willSet {
if frame.equalTo(newValue) == false && newValue.equalTo(CGRect.zero) == false && imageSize.equalTo(CGSize.zero) == false { if frame.equalTo(newValue) == false && newValue.equalTo(CGRect.zero) == false && imageSize.equalTo(CGSize.zero) == false {
@ -93,16 +97,14 @@ open class ImageScrollView: UIScrollView {
// center horizontally // center horizontally
if frameToCenter.size.width < bounds.width { if frameToCenter.size.width < bounds.width {
frameToCenter.origin.x = (bounds.width - frameToCenter.size.width) / 2 frameToCenter.origin.x = (bounds.width - frameToCenter.size.width) / 2
} } else {
else {
frameToCenter.origin.x = 0 frameToCenter.origin.x = 0
} }
// center vertically // center vertically
if frameToCenter.size.height < bounds.height { if frameToCenter.size.height < bounds.height {
frameToCenter.origin.y = (bounds.height - frameToCenter.size.height) / 2 frameToCenter.origin.y = (bounds.height - frameToCenter.size.height) / 2
} } else {
else {
frameToCenter.origin.y = 0 frameToCenter.origin.y = 0
} }
@ -260,8 +262,7 @@ open class ImageScrollView: UIScrollView {
// zoom out if it bigger than middle scale point. Else, zoom in // zoom out if it bigger than middle scale point. Else, zoom in
if zoomScale >= maximumZoomScale / 2.0 { if zoomScale >= maximumZoomScale / 2.0 {
setZoomScale(minimumZoomScale, animated: true) setZoomScale(minimumZoomScale, animated: true)
} } else {
else {
let center = gestureRecognizer.location(in: gestureRecognizer.view) let center = gestureRecognizer.location(in: gestureRecognizer.view)
let zoomRect = zoomRectForScale(ImageScrollView.kZoomInFactorFromMinWhenDoubleTap * minimumZoomScale, center: center) let zoomRect = zoomRectForScale(ImageScrollView.kZoomInFactorFromMinWhenDoubleTap * minimumZoomScale, center: center)
zoom(to: zoomRect, animated: true) zoom(to: zoomRect, animated: true)

View File

@ -0,0 +1,66 @@
//
// ImageAnimator.swift
// NetNewsWire-iOS
//
// Created by Maurice Parker on 10/15/19.
// Copyright © 2019 Ranchero Software. All rights reserved.
//
import UIKit
class ImageTransition: NSObject, UIViewControllerAnimatedTransitioning {
let duration = 0.5
var presenting = true
var originFrame: CGRect!
var originImage: UIImage!
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return duration
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
let destFrame: CGRect = {
if presenting {
let imageController = transitionContext.viewController(forKey: .to) as! ImageViewController
return imageController.zoomedFrame
} else {
let imageController = transitionContext.viewController(forKey: .from) as! ImageViewController
return imageController.zoomedFrame
}
}()
let initialFrame = presenting ? originFrame! : destFrame
let targetFrame = presenting ? destFrame : originFrame!
let imageView = UIImageView(image: originImage)
imageView.frame = initialFrame
// let xScaleFactor = presenting ? initialFrame.width / targetFrame.width : targetFrame.width / initialFrame.width
// let yScaleFactor = presenting ? initialFrame.height / targetFrame.height : targetFrame.height / initialFrame.height
// let scaleTransform = CGAffineTransform(scaleX: xScaleFactor, y: yScaleFactor)
let fromView = transitionContext.view(forKey: .from)!
fromView.removeFromSuperview()
transitionContext.containerView.backgroundColor = UIColor.systemBackground
transitionContext.containerView.addSubview(imageView)
UIView.animate(
withDuration: duration,
animations: {
imageView.frame = targetFrame
// imageView.transform = scaleTransform
// imageView.center = CGPoint(x: targetFrame.midX, y: targetFrame.midY)
}, completion: { _ in
imageView.removeFromSuperview()
let toView = transitionContext.view(forKey: .to)!
transitionContext.containerView.addSubview(toView)
transitionContext.containerView.bringSubviewToFront(toView)
transitionContext.completeTransition(true)
})
}
}

View File

@ -14,6 +14,9 @@ class ImageViewController: UIViewController {
@IBOutlet weak var imageScrollView: ImageScrollView! @IBOutlet weak var imageScrollView: ImageScrollView!
var image: UIImage! var image: UIImage!
var zoomedFrame: CGRect {
return imageScrollView.zoomedFrame
}
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()