diff --git a/Transitions/ZoomAnimator.swift b/Transitions/ZoomAnimator.swift index 5e0b041..b888711 100644 --- a/Transitions/ZoomAnimator.swift +++ b/Transitions/ZoomAnimator.swift @@ -50,6 +50,7 @@ private extension ZoomAnimator { if transitionView == nil, let transitionView = (fromReferenceView as? ZoomAnimatableView)?.transitionView() { transitionView.frame = fromReferenceViewFrame + transitionView.layer.contentsRect = fromReferenceView.layer.contentsRect self.transitionView = transitionView transitionContext.containerView.addSubview(transitionView) } @@ -65,6 +66,7 @@ private extension ZoomAnimator { initialSpringVelocity: 0, options: [.transitionCrossDissolve]) { self.transitionView?.frame = finalTransitionSize + self.transitionView?.layer.contentsRect = .defaultContentsRect toVC.view.alpha = 1.0 fromVC.tabBarController?.tabBar.alpha = 0 } completion: { _ in @@ -114,6 +116,8 @@ private extension ZoomAnimator { self.transitionView?.alpha = 0 } + self.transitionView?.layer.contentsRect = toReferenceView?.layer.contentsRect ?? .defaultContentsRect + toVC.tabBarController?.tabBar.alpha = 1 } completion: { _ in self.transitionView?.removeFromSuperview() diff --git a/Transitions/ZoomDismissalInteractionController.swift b/Transitions/ZoomDismissalInteractionController.swift index 71968fb..3130732 100644 --- a/Transitions/ZoomDismissalInteractionController.swift +++ b/Transitions/ZoomDismissalInteractionController.swift @@ -83,6 +83,8 @@ final class ZoomDismissalInteractionController: NSObject { transitionView.alpha = 0 } + transitionView.layer.contentsRect = toReferenceView?.layer.contentsRect ?? .defaultContentsRect + toVC.tabBarController?.tabBar.alpha = 1 } completion: { _ in transitionView.removeFromSuperview() diff --git a/Views/Status/StatusAttachmentView.swift b/Views/Status/StatusAttachmentView.swift index 4202b2b..8de86da 100644 --- a/Views/Status/StatusAttachmentView.swift +++ b/Views/Status/StatusAttachmentView.swift @@ -39,6 +39,37 @@ final class StatusAttachmentView: UIView { required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } + + override func layoutSubviews() { + super.layoutSubviews() + + if let focus = viewModel.attachment.meta?.focus { + let viewsAndSizes: [(UIView, CGSize?)] = [ + (imageView, imageView.image?.size), + (playerView, playerView.player?.currentItem?.presentationSize)] + for (view, size) in viewsAndSizes { + guard let size = size else { continue } + + let aspectRatio = size.width / size.height + let viewAspectRatio = view.frame.width / view.frame.height + var origin = CGPoint.zero + + if viewAspectRatio > aspectRatio { + let mediaProportionalHeight = size.height * view.frame.width / size.width + let maxPan = (mediaProportionalHeight - view.frame.height) / (2 * mediaProportionalHeight) + + origin.y = CGFloat(-focus.y) * maxPan + } else { + let mediaProportionalWidth = size.width * view.frame.height / size.height + let maxPan = (mediaProportionalWidth - view.frame.width) / (2 * mediaProportionalWidth) + + origin.x = CGFloat(focus.x) * maxPan + } + + view.layer.contentsRect = .init(origin: origin, size: CGRect.defaultContentsRect.size) + } + } + } } extension StatusAttachmentView { @@ -114,7 +145,11 @@ private extension StatusAttachmentView { switch viewModel.attachment.type { case .image, .video, .gifv: - imageView.kf.setImage(with: viewModel.attachment.previewUrl) + imageView.kf.setImage( + with: viewModel.attachment.previewUrl, + completionHandler: { [weak self] _ in + self?.layoutSubviews() + }) case .audio: playImageView.image = UIImage(systemName: "waveform.circle", withConfiguration: UIImage.SymbolConfiguration(textStyle: .largeTitle)) diff --git a/Views/ViewConstants.swift b/Views/ViewConstants.swift index 9cb394a..397f503 100644 --- a/Views/ViewConstants.swift +++ b/Views/ViewConstants.swift @@ -15,6 +15,10 @@ extension CGFloat { static let newStatusButtonShadowRadius: CGFloat = 2 } +extension CGRect { + static let defaultContentsRect = Self(origin: .zero, size: .init(width: 1, height: 1)) +} + extension TimeInterval { static let defaultAnimationDuration: Self = 0.5 static let shortAnimationDuration = defaultAnimationDuration / 2