mirror of
https://github.com/Ranchero-Software/NetNewsWire.git
synced 2024-12-23 08:09:41 +01:00
Animate the transition to full screen image view
This commit is contained in:
parent
6ae36303de
commit
425b3b09a1
@ -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 */,
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
66
iOS/Article/ImageTransition.swift
Normal file
66
iOS/Article/ImageTransition.swift
Normal 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)
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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()
|
||||||
|
Loading…
Reference in New Issue
Block a user