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 {
|
||||
static let imageWasClicked = "imageWasClicked"
|
||||
static let imageWasShown = "imageWasShown"
|
||||
}
|
||||
|
||||
@IBOutlet private weak var nextUnreadBarButtonItem: UIBarButtonItem!
|
||||
|
@ -43,7 +44,8 @@ class ArticleViewController: UIViewController {
|
|||
}()
|
||||
|
||||
private var webView: WKWebView!
|
||||
private var transition = ImageTransition()
|
||||
private lazy var transition = ImageTransition(controller: self)
|
||||
private var clickedImageCompletion: (() -> Void)?
|
||||
|
||||
weak var coordinator: SceneCoordinator!
|
||||
|
||||
|
@ -109,7 +111,9 @@ class ArticleViewController: UIViewController {
|
|||
webView.uiDelegate = self
|
||||
|
||||
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.imageWasShown)
|
||||
|
||||
// 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
|
||||
|
@ -296,6 +300,15 @@ class ArticleViewController: UIViewController {
|
|||
webView.scrollView.setContentOffset(scrollToPoint, animated: true)
|
||||
}
|
||||
|
||||
func hideClickedImage() {
|
||||
webView?.evaluateJavaScript("hideClickedImage();")
|
||||
}
|
||||
|
||||
func showClickedImage(completion: @escaping () -> Void) {
|
||||
clickedImageCompletion = completion
|
||||
webView?.evaluateJavaScript("showClickedImage();")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: WKNavigationDelegate
|
||||
|
@ -350,26 +363,13 @@ extension ArticleViewController: WKUIDelegate {
|
|||
extension ArticleViewController: WKScriptMessageHandler {
|
||||
|
||||
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
|
||||
if message.name == MessageName.imageWasClicked,
|
||||
let body = message.body as? String,
|
||||
let data = body.data(using: .utf8),
|
||||
let clickMessage = try? JSONDecoder().decode(ImageClickMessage.self, from: data),
|
||||
let range = clickMessage.imageURL.range(of: ";base64,") {
|
||||
|
||||
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)
|
||||
|
||||
}
|
||||
switch message.name {
|
||||
case MessageName.imageWasShown:
|
||||
clickedImageCompletion?()
|
||||
case MessageName.imageWasClicked:
|
||||
imageWasClicked(body: message.body as? String)
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
||||
let duration = 0.4
|
||||
private weak var articleController: ArticleViewController?
|
||||
private let duration = 0.4
|
||||
var presenting = true
|
||||
var originFrame: CGRect!
|
||||
var originImage: UIImage!
|
||||
|
||||
init(controller: ArticleViewController) {
|
||||
self.articleController = controller
|
||||
}
|
||||
|
||||
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
|
||||
animateTransitionPresenting(using: transitionContext)
|
||||
} else {
|
||||
let imageController = transitionContext.viewController(forKey: .from) as! ImageViewController
|
||||
return imageController.zoomedFrame
|
||||
animateTransitionReturning(using: transitionContext)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
let initialFrame = presenting ? originFrame! : destFrame
|
||||
let targetFrame = presenting ? destFrame : originFrame!
|
||||
private func animateTransitionPresenting(using transitionContext: UIViewControllerContextTransitioning) {
|
||||
|
||||
let imageView = UIImageView(image: originImage)
|
||||
imageView.frame = initialFrame
|
||||
imageView.frame = originFrame
|
||||
|
||||
let fromView = transitionContext.view(forKey: .from)!
|
||||
fromView.removeFromSuperview()
|
||||
|
||||
if presenting {
|
||||
|
||||
transitionContext.containerView.backgroundColor = UIColor.systemBackground
|
||||
transitionContext.containerView.addSubview(imageView)
|
||||
|
||||
articleController?.hideClickedImage()
|
||||
|
||||
UIView.animate(
|
||||
withDuration: duration,
|
||||
delay:0.0,
|
||||
usingSpringWithDamping: 0.8,
|
||||
initialSpringVelocity: 0.2,
|
||||
animations: {
|
||||
let imageController = transitionContext.viewController(forKey: .to) as! ImageViewController
|
||||
imageView.frame = imageController.zoomedFrame
|
||||
}, completion: { _ in
|
||||
imageView.removeFromSuperview()
|
||||
let toView = transitionContext.view(forKey: .to)!
|
||||
transitionContext.containerView.addSubview(toView)
|
||||
transitionContext.completeTransition(true)
|
||||
})
|
||||
}
|
||||
|
||||
private func animateTransitionReturning(using transitionContext: UIViewControllerContextTransitioning) {
|
||||
let imageController = transitionContext.viewController(forKey: .from) as! ImageViewController
|
||||
let imageView = UIImageView(image: originImage)
|
||||
imageView.frame = imageController.zoomedFrame
|
||||
|
||||
let fromView = transitionContext.view(forKey: .from)!
|
||||
fromView.removeFromSuperview()
|
||||
|
||||
let toView = transitionContext.view(forKey: .to)!
|
||||
transitionContext.containerView.addSubview(toView)
|
||||
transitionContext.containerView.addSubview(imageView)
|
||||
|
||||
UIView.animate(
|
||||
withDuration: duration,
|
||||
delay:0.0,
|
||||
usingSpringWithDamping: 0.8,
|
||||
initialSpringVelocity: 0.2,
|
||||
animations: {
|
||||
imageView.frame = targetFrame
|
||||
}, completion: { _ in
|
||||
imageView.removeFromSuperview()
|
||||
let toView = transitionContext.view(forKey: .to)!
|
||||
transitionContext.containerView.addSubview(toView)
|
||||
transitionContext.completeTransition(true)
|
||||
})
|
||||
|
||||
} else {
|
||||
|
||||
let toView = transitionContext.view(forKey: .to)!
|
||||
transitionContext.containerView.addSubview(toView)
|
||||
transitionContext.containerView.addSubview(imageView)
|
||||
|
||||
UIView.animate(
|
||||
withDuration: duration,
|
||||
delay:0.0,
|
||||
animations: {
|
||||
imageView.frame = targetFrame
|
||||
imageView.alpha = 0
|
||||
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
|
||||
async function imageWasClicked(img) {
|
||||
img.classList.add("nnwClicked");
|
||||
|
||||
const rect = img.getBoundingClientRect();
|
||||
|
||||
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() {
|
||||
window.onclick = function(event) {
|
||||
if (event.target.matches('img')) {
|
||||
|
|
Loading…
Reference in New Issue