Change image zoom animation to be a solid picture zooming in and out
This commit is contained in:
parent
3ddd14d856
commit
cb6490222f
@ -24,6 +24,7 @@ class ArticleViewController: UIViewController {
|
|||||||
|
|
||||||
private struct MessageName {
|
private struct MessageName {
|
||||||
static let imageWasClicked = "imageWasClicked"
|
static let imageWasClicked = "imageWasClicked"
|
||||||
|
static let imageWasShown = "imageWasShown"
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBOutlet private weak var nextUnreadBarButtonItem: UIBarButtonItem!
|
@IBOutlet private weak var nextUnreadBarButtonItem: UIBarButtonItem!
|
||||||
@ -43,7 +44,8 @@ class ArticleViewController: UIViewController {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
private var webView: WKWebView!
|
private var webView: WKWebView!
|
||||||
private var transition = ImageTransition()
|
private lazy var transition = ImageTransition(controller: self)
|
||||||
|
private var clickedImageCompletion: (() -> Void)?
|
||||||
|
|
||||||
weak var coordinator: SceneCoordinator!
|
weak var coordinator: SceneCoordinator!
|
||||||
|
|
||||||
@ -109,7 +111,9 @@ class ArticleViewController: UIViewController {
|
|||||||
webView.uiDelegate = self
|
webView.uiDelegate = self
|
||||||
|
|
||||||
webView.configuration.userContentController.removeScriptMessageHandler(forName: MessageName.imageWasClicked)
|
webView.configuration.userContentController.removeScriptMessageHandler(forName: MessageName.imageWasClicked)
|
||||||
|
webView.configuration.userContentController.removeScriptMessageHandler(forName: MessageName.imageWasShown)
|
||||||
webView.configuration.userContentController.add(WrapperScriptMessageHandler(self), name: MessageName.imageWasClicked)
|
webView.configuration.userContentController.add(WrapperScriptMessageHandler(self), name: MessageName.imageWasClicked)
|
||||||
|
webView.configuration.userContentController.add(WrapperScriptMessageHandler(self), name: MessageName.imageWasShown)
|
||||||
|
|
||||||
// Even though page.html should be loaded into this webview, we have to do it again
|
// Even though page.html should be loaded into this webview, we have to do it again
|
||||||
// to work around this bug: http://www.openradar.me/22855188
|
// to work around this bug: http://www.openradar.me/22855188
|
||||||
@ -296,6 +300,15 @@ class ArticleViewController: UIViewController {
|
|||||||
webView.scrollView.setContentOffset(scrollToPoint, animated: true)
|
webView.scrollView.setContentOffset(scrollToPoint, animated: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func hideClickedImage() {
|
||||||
|
webView?.evaluateJavaScript("hideClickedImage();")
|
||||||
|
}
|
||||||
|
|
||||||
|
func showClickedImage(completion: @escaping () -> Void) {
|
||||||
|
clickedImageCompletion = completion
|
||||||
|
webView?.evaluateJavaScript("showClickedImage();")
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: WKNavigationDelegate
|
// MARK: WKNavigationDelegate
|
||||||
@ -350,26 +363,13 @@ extension ArticleViewController: WKUIDelegate {
|
|||||||
extension ArticleViewController: WKScriptMessageHandler {
|
extension ArticleViewController: WKScriptMessageHandler {
|
||||||
|
|
||||||
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
|
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
|
||||||
if message.name == MessageName.imageWasClicked,
|
switch message.name {
|
||||||
let body = message.body as? String,
|
case MessageName.imageWasShown:
|
||||||
let data = body.data(using: .utf8),
|
clickedImageCompletion?()
|
||||||
let clickMessage = try? JSONDecoder().decode(ImageClickMessage.self, from: data),
|
case MessageName.imageWasClicked:
|
||||||
let range = clickMessage.imageURL.range(of: ";base64,") {
|
imageWasClicked(body: message.body as? String)
|
||||||
|
default:
|
||||||
let base64Image = String(clickMessage.imageURL.suffix(from: range.upperBound))
|
return
|
||||||
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))
|
|
||||||
transition.originFrame = webView.convert(rect, to: nil)
|
|
||||||
transition.originImage = image
|
|
||||||
|
|
||||||
let imageVC = UIStoryboard.main.instantiateController(ofType: ImageViewController.self)
|
|
||||||
imageVC.image = image
|
|
||||||
imageVC.modalPresentationStyle = .fullScreen
|
|
||||||
imageVC.transitioningDelegate = self
|
|
||||||
present(imageVC, animated: true)
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -430,4 +430,25 @@ private extension ArticleViewController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func imageWasClicked(body: String?) {
|
||||||
|
guard let body = body,
|
||||||
|
let data = body.data(using: .utf8),
|
||||||
|
let clickMessage = try? JSONDecoder().decode(ImageClickMessage.self, from: data),
|
||||||
|
let range = clickMessage.imageURL.range(of: ";base64,")
|
||||||
|
else { return }
|
||||||
|
|
||||||
|
let base64Image = String(clickMessage.imageURL.suffix(from: range.upperBound))
|
||||||
|
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))
|
||||||
|
transition.originFrame = webView.convert(rect, to: nil)
|
||||||
|
transition.originImage = image
|
||||||
|
|
||||||
|
let imageVC = UIStoryboard.main.instantiateController(ofType: ImageViewController.self)
|
||||||
|
imageVC.image = image
|
||||||
|
imageVC.modalPresentationStyle = .fullScreen
|
||||||
|
imageVC.transitioningDelegate = self
|
||||||
|
present(imageVC, animated: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -10,74 +10,84 @@ import UIKit
|
|||||||
|
|
||||||
class ImageTransition: NSObject, UIViewControllerAnimatedTransitioning {
|
class ImageTransition: NSObject, UIViewControllerAnimatedTransitioning {
|
||||||
|
|
||||||
let duration = 0.4
|
private weak var articleController: ArticleViewController?
|
||||||
|
private let duration = 0.4
|
||||||
var presenting = true
|
var presenting = true
|
||||||
var originFrame: CGRect!
|
var originFrame: CGRect!
|
||||||
var originImage: UIImage!
|
var originImage: UIImage!
|
||||||
|
|
||||||
|
init(controller: ArticleViewController) {
|
||||||
|
self.articleController = controller
|
||||||
|
}
|
||||||
|
|
||||||
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
|
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
|
||||||
return duration
|
return duration
|
||||||
}
|
}
|
||||||
|
|
||||||
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
|
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
|
||||||
|
if presenting {
|
||||||
|
animateTransitionPresenting(using: transitionContext)
|
||||||
|
} else {
|
||||||
|
animateTransitionReturning(using: transitionContext)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let destFrame: CGRect = {
|
private func animateTransitionPresenting(using transitionContext: UIViewControllerContextTransitioning) {
|
||||||
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)
|
let imageView = UIImageView(image: originImage)
|
||||||
imageView.frame = initialFrame
|
imageView.frame = originFrame
|
||||||
|
|
||||||
let fromView = transitionContext.view(forKey: .from)!
|
let fromView = transitionContext.view(forKey: .from)!
|
||||||
fromView.removeFromSuperview()
|
fromView.removeFromSuperview()
|
||||||
|
|
||||||
if presenting {
|
transitionContext.containerView.backgroundColor = UIColor.systemBackground
|
||||||
|
transitionContext.containerView.addSubview(imageView)
|
||||||
|
|
||||||
transitionContext.containerView.backgroundColor = UIColor.systemBackground
|
articleController?.hideClickedImage()
|
||||||
transitionContext.containerView.addSubview(imageView)
|
|
||||||
|
|
||||||
UIView.animate(
|
UIView.animate(
|
||||||
withDuration: duration,
|
withDuration: duration,
|
||||||
delay:0.0,
|
delay:0.0,
|
||||||
usingSpringWithDamping: 0.8,
|
usingSpringWithDamping: 0.8,
|
||||||
initialSpringVelocity: 0.2,
|
initialSpringVelocity: 0.2,
|
||||||
animations: {
|
animations: {
|
||||||
imageView.frame = targetFrame
|
let imageController = transitionContext.viewController(forKey: .to) as! ImageViewController
|
||||||
}, completion: { _ in
|
imageView.frame = imageController.zoomedFrame
|
||||||
imageView.removeFromSuperview()
|
}, completion: { _ in
|
||||||
let toView = transitionContext.view(forKey: .to)!
|
imageView.removeFromSuperview()
|
||||||
transitionContext.containerView.addSubview(toView)
|
let toView = transitionContext.view(forKey: .to)!
|
||||||
transitionContext.completeTransition(true)
|
transitionContext.containerView.addSubview(toView)
|
||||||
})
|
transitionContext.completeTransition(true)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
private func animateTransitionReturning(using transitionContext: UIViewControllerContextTransitioning) {
|
||||||
|
let imageController = transitionContext.viewController(forKey: .from) as! ImageViewController
|
||||||
|
let imageView = UIImageView(image: originImage)
|
||||||
|
imageView.frame = imageController.zoomedFrame
|
||||||
|
|
||||||
let toView = transitionContext.view(forKey: .to)!
|
let fromView = transitionContext.view(forKey: .from)!
|
||||||
transitionContext.containerView.addSubview(toView)
|
fromView.removeFromSuperview()
|
||||||
transitionContext.containerView.addSubview(imageView)
|
|
||||||
|
|
||||||
UIView.animate(
|
let toView = transitionContext.view(forKey: .to)!
|
||||||
withDuration: duration,
|
transitionContext.containerView.addSubview(toView)
|
||||||
delay:0.0,
|
transitionContext.containerView.addSubview(imageView)
|
||||||
animations: {
|
|
||||||
imageView.frame = targetFrame
|
|
||||||
imageView.alpha = 0
|
|
||||||
}, completion: { _ in
|
|
||||||
imageView.removeFromSuperview()
|
|
||||||
transitionContext.completeTransition(true)
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
UIView.animate(
|
||||||
|
withDuration: duration,
|
||||||
|
delay:0.0,
|
||||||
|
usingSpringWithDamping: 0.8,
|
||||||
|
initialSpringVelocity: 0.2,
|
||||||
|
animations: {
|
||||||
|
imageView.frame = self.originFrame
|
||||||
|
}, completion: { _ in
|
||||||
|
self.articleController?.showClickedImage() {
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||||
|
imageView.removeFromSuperview()
|
||||||
|
transitionContext.completeTransition(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
// Used to pop a resizable image view
|
// Used to pop a resizable image view
|
||||||
async function imageWasClicked(img) {
|
async function imageWasClicked(img) {
|
||||||
|
img.classList.add("nnwClicked");
|
||||||
|
|
||||||
const rect = img.getBoundingClientRect();
|
const rect = img.getBoundingClientRect();
|
||||||
|
|
||||||
var message = {
|
var message = {
|
||||||
@ -31,7 +33,21 @@ async function imageWasClicked(img) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the click listeners for images
|
// Used to animate the transition to a fullscreen image
|
||||||
|
function hideClickedImage() {
|
||||||
|
var img = document.querySelector('.nnwClicked')
|
||||||
|
img.style.opacity = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used to animate the transition from a fullscreen image
|
||||||
|
function showClickedImage() {
|
||||||
|
var img = document.querySelector('.nnwClicked')
|
||||||
|
img.classList.remove("nnwClicked");
|
||||||
|
img.style.opacity = 1
|
||||||
|
window.webkit.messageHandlers.imageWasShown.postMessage("");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the click listener for images
|
||||||
function imageClicks() {
|
function imageClicks() {
|
||||||
window.onclick = function(event) {
|
window.onclick = function(event) {
|
||||||
if (event.target.matches('img')) {
|
if (event.target.matches('img')) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user