Я пытаюсь анимировать переход от UICollectionViewController к UIViewController.
TLDR: в последней функции, как я могу получить кадр, который будет учитывать панель навигации?
Каждая ячейка имеет UIImageView, и я хочу увеличить его при переходе к контроллеру представления. Это похоже на приложение «Фото», за исключением того, что изображение не отцентрировано, и вокруг него есть другие виды (надписи, и т. Д. c.).
UIImageView размещается с использованием AutoLayout, центрируется по горизонтали и на 89 точек из верхнее безопасное руководство по компоновке.
Это почти работает, за исключением того, что я не могу получить правильный кадр для целевого UIImageView, он не учитывает навигацию, и поэтому начало кадра у отключено столько же.
Мой UICollectionViewController
- это UINavigationControllerDelegate
class CollectionViewController: UINavigationControllerDelegate {
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard segue.identifier == detailSegue,
let destination = segue.destination as? CustomViewController,
let indexPath = sender as? IndexPath else { return }
navigationController?.delegate = self
…
}
func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationController.Operation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
switch operation {
case .push:
return ZoomInAnimator()
default:
return nil
}
}
}
Объект ZoomInAnimator
выглядит так:
open class ZoomInAnimator: NSObject, UIViewControllerAnimatedTransitioning {
public func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.5
}
public func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard let fromViewController = transitionContext.viewController(forKey: .from),
let fromContent = (fromViewController as? ZoomAnimatorDelegate)?.animatedContent(),
let toViewController = transitionContext.viewController(forKey: .to),
let toContent = (toViewController as? ZoomAnimatorDelegate)?.animatedContent() else { return }
let finalFrame = toContent.pictureFrame!
// Set pre-animation state
toViewController.view.alpha = 0
toContent.picture.isHidden = true
// Add a new UIImageView where the "from" picture currently is
let transitionImageView = UIImageView(frame: fromContent.pictureFrame)
transitionImageView.image = fromContent.picture.image
transitionImageView.contentMode = fromContent.picture.contentMode
transitionImageView.clipsToBounds = fromContent.picture.clipsToBounds
transitionContext.containerView.addSubview(toViewController.view)
transitionContext.containerView.addSubview(transitionImageView)
// Animate the transition
UIView.animate(
withDuration: transitionDuration(using: transitionContext),
delay: 0,
usingSpringWithDamping: 0.8,
initialSpringVelocity: 0,
options: [.transitionCrossDissolve],
animations: {
transitionImageView.frame = finalFrame
toViewController.view.alpha = 1
}) { completed in
transitionImageView.removeFromSuperview()
toContent.picture.isHidden = false
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
}
}
}
Как видите, Я спрашиваю оба контроллера для их UIImageViews и их фреймов, используя animatedContent()
. Эта функция определена как в UICollectionViewController, так и в UIViewController, которые оба соответствуют этому протоколу:
protocol ZoomAnimatorDelegate {
func animatedContent() -> AnimatedContent?
}
, где AnimatedContent
представляет собой простую структуру:
struct AnimatedContent {
let picture: UIImageView!
let pictureFrame: CGRect!
}
Вот как эта функция реализован с обеих сторон.
Эта сторона отлично работает (UICollectionViewController / fromViewController в переходе):
extension CollectionViewController: ZoomAnimatorDelegate {
func animatedContent() -> AnimatedContent? {
guard let indexPath = collectionView.indexPathsForSelectedItems?.first else { return nil }
let cell = cellOrPlaceholder(for: indexPath) // Custom method to get the cell, I'll skip the details
let frame = cell.convert(cell.picture.frame, to: view)
return AnimatedContent(picture: cell.picture, pictureFrame: frame)
}
}
Это проблематичная c часть.
Начало кадра изображения составляет 89 пт и игнорирует панель навигации, которая добавляет смещение 140 пт. Я пытался все виды конвертировать (в :) без какого-либо успеха. Кроме того, я немного обеспокоен вызовом view.layoutIfNeeded
здесь, это способ сделать это?
class ViewController: ZoomAnimatorDelegate {
@IBOutlet weak var picture: UIImageView!
func animatedContent() -> AnimatedContent? {
view.layoutIfNeeded()
return AnimatedContent(picture: picture, profilePictureFrame: picture.frame)
}
}
РЕДАКТИРОВАТЬ: Вот обновленная версия animateTransition
(пока не используются снимки , но я доберусь туда)
public func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard let fromViewController = transitionContext.viewController(forKey: .from) as? CollectionViewController,
let fromContent = fromViewController.animatedContent(),
let toViewController = transitionContext.viewController(forKey: .to) as? ViewController,
let toImageView = toViewController.picture else { return }
transitionContext.containerView.addSubview(toViewController.view)
toViewController.view.frame = transitionContext.finalFrame(for: toViewController)
toViewController.view.setNeedsLayout()
toViewController.view.layoutIfNeeded()
let finalFrame = toViewController.view.convert(toImageView.frame, to: transitionContext.containerView)
let transitionImageView = UIImageView(frame: fromContent.pictureFrame)
transitionImageView.image = fromContent.picture.image
transitionImageView.contentMode = fromContent.picture.contentMode
transitionImageView.clipsToBounds = fromContent.picture.clipsToBounds
transitionContext.containerView.addSubview(transitionImageView)
// Set pre-animation state
toViewController.view.alpha = 0
toImageView.isHidden = true
UIView.animate(
withDuration: transitionDuration(using: transitionContext),
delay: 0,
usingSpringWithDamping: 0.8,
initialSpringVelocity: 0,
options: [.transitionCrossDissolve],
animations: {
transitionImageView.frame = finalFrame
toViewController.view.alpha = 1
}) { completed in
transitionImageView.removeFromSuperview()
toImageView.isHidden = false
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
}
}