gpt4 book ai didi

ios - View Controller Transition 动画 subview 位置

转载 作者:可可西里 更新时间:2023-11-01 05:01:03 27 4
gpt4 key购买 nike

我正在尝试在两个 View Controller 之间创建一个简单的过渡动画,这两个 View Controller 具有相同的标签。我只是想将标签从它在第一个 View Controller 中的位置动画化到它在第二个 View Controller 中的位置(见下图)。

View Controller Illustration

我已将我的 View Controller 设置为使用自定义动画 Controller ,我可以通过 socket 访问 View Controller 和标签。

在动画 block 中,我只是将第一个 View Controller 上标签的框架设置为第二个 View Controller 上标签的框架。

[UIView animateWithDuration:self.duration animations:^{
fromViewController.label.frame = toViewController.titleLabel.frame;
} completion:^(BOOL finished) {
[transitionContext completeTransition:finished];
}];

与标签从屏幕中间移动到左上角的预期效果不同,动画一开始标签就位于右下角,然后动画到中间。

我尝试预先打印出标签的位置,它显示了我在 Storyboard 中看到的同一帧:

fromViewController.label.frame: {{115.5, 313}, {144, 41}}
toViewController.titleLabel.frame: {{16, 12}, {144, 41}}

我不知道为什么我没有得到预期的行为,也不知道发生了什么。

对于我可以更改哪些内容以使我的动画正确运行以及为什么我会看到此行为的任何建议,我们将不胜感激。

最佳答案

你提到了 subview 的动画,但你没有谈论整体动画,但我倾向于使用容器 View 来制作动画,以避免在为 subview 设置动画时出现任何潜在的混淆/问题同时显示主视图。但我倾向于:

  1. 对“来自” View 中 subview 的位置进行快照,然后隐藏 subview ;
  2. 对“to” View 中 subview 的位置进行快照,然后隐藏 subview ;
  3. 将所有这些frame值转换为容器的坐标空间,并将所有这些快照添加到容器 View ;
  4. 从零开始“到”快照的 alpha(因此它们淡入);
  5. 动画将“to”快照更改为最终目的地,将它们的 alpha 变回 1
  6. 同时将“从”快照动画化到“到” View 最终目的地的位置并将它们的 alpha 动画化为零(因此它们淡出,结合第 4 点,产生一种交叉溶解)。
  7. 完成所有操作后,删除快照并取消隐藏其快照已设置动画的 subview 。

最终效果是标签从一个位置滑动到另一个位置,如果初始内容和最终内容不同,则在它们移动时会产生交叉渐隐。

例如:

enter image description here

通过将容器 View 用于快照动画,它独立于您可能对目标场景的主视图执行的任何动画。在本例中,我将它从右侧滑入,但您可以随心所欲。

或者,您可以对多个 subview 执行此操作:

enter image description here

(就个人而言,如果是这种情况,几乎所有东西都在滑动,我会失去主视图的滑动动画,因为它现在变得分散注意力,但它给了你基本的想法。另外,在我的关闭动画中,我把哪个 View 换成另一个 View ,这是你永远不会做的,但我只是想说明灵 active 和褪色。)

为了呈现以上内容,我在 Swift 4 中使用了以下内容:

protocol CustomTransitionOriginator {
var fromAnimatedSubviews: [UIView] { get }
}

protocol CustomTransitionDestination {
var toAnimatedSubviews: [UIView] { get }
}

class Animator: NSObject, UIViewControllerAnimatedTransitioning {
enum TransitionType {
case present
case dismiss
}

let type: TransitionType

init(type: TransitionType) {
self.type = type
super.init()
}

func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 1.0
}

func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
let fromVC = transitionContext.viewController(forKey: .from) as! CustomTransitionOriginator & UIViewController
let toVC = transitionContext.viewController(forKey: .to) as! CustomTransitionDestination & UIViewController

let container = transitionContext.containerView

// add the "to" view to the hierarchy

toVC.view.frame = fromVC.view.frame
if type == .present {
container.addSubview(toVC.view)
} else {
container.insertSubview(toVC.view, belowSubview: fromVC.view)
}
toVC.view.layoutIfNeeded()

// create snapshots of label being animated

let fromSnapshots = fromVC.fromAnimatedSubviews.map { subview -> UIView in
// create snapshot

let snapshot = subview.snapshotView(afterScreenUpdates: false)!

// we're putting it in container, so convert original frame into container's coordinate space

snapshot.frame = container.convert(subview.frame, from: subview.superview)

return snapshot
}

let toSnapshots = toVC.toAnimatedSubviews.map { subview -> UIView in
// create snapshot

let snapshot = subview.snapshotView(afterScreenUpdates: true)!// UIImageView(image: subview.snapshot())

// we're putting it in container, so convert original frame into container's coordinate space

snapshot.frame = container.convert(subview.frame, from: subview.superview)

return snapshot
}

// save the "to" and "from" frames

let frames = zip(fromSnapshots, toSnapshots).map { ($0.frame, $1.frame) }

// move the "to" snapshots to where where the "from" views were, but hide them for now

zip(toSnapshots, frames).forEach { snapshot, frame in
snapshot.frame = frame.0
snapshot.alpha = 0
container.addSubview(snapshot)
}

// add "from" snapshots, too, but hide the subviews that we just snapshotted
// associated labels so we only see animated snapshots; we'll unhide these
// original views when the animation is done.

fromSnapshots.forEach { container.addSubview($0) }
fromVC.fromAnimatedSubviews.forEach { $0.alpha = 0 }
toVC.toAnimatedSubviews.forEach { $0.alpha = 0 }

// I'm going to push the the main view from the right and dim the "from" view a bit,
// but you'll obviously do whatever you want for the main view, if anything

if type == .present {
toVC.view.transform = .init(translationX: toVC.view.frame.width, y: 0)
} else {
toVC.view.alpha = 0.5
}

// do the animation

UIView.animate(withDuration: transitionDuration(using: transitionContext), animations: {
// animate the snapshots of the label

zip(toSnapshots, frames).forEach { snapshot, frame in
snapshot.frame = frame.1
snapshot.alpha = 1
}

zip(fromSnapshots, frames).forEach { snapshot, frame in
snapshot.frame = frame.1
snapshot.alpha = 0
}

// I'm now animating the "to" view into place, but you'd do whatever you want here

if self.type == .present {
toVC.view.transform = .identity
fromVC.view.alpha = 0.5
} else {
fromVC.view.transform = .init(translationX: fromVC.view.frame.width, y: 0)
toVC.view.alpha = 1
}
}, completion: { _ in
// get rid of snapshots and re-show the original labels

fromSnapshots.forEach { $0.removeFromSuperview() }
toSnapshots.forEach { $0.removeFromSuperview() }
fromVC.fromAnimatedSubviews.forEach { $0.alpha = 1 }
toVC.toAnimatedSubviews.forEach { $0.alpha = 1 }

// clean up "to" and "from" views as necessary, in my case, just restore "from" view's alpha

fromVC.view.alpha = 1
fromVC.view.transform = .identity

// complete the transition

transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
})
}
}

// My `UIViewControllerTransitioningDelegate` will specify this presentation
// controller, which will clean out the "from" view from the hierarchy when
// the animation is done.

class PresentationController: UIPresentationController {
override var shouldRemovePresentersView: Bool { return true }
}

然后,为了让以上所有的工作正常,如果我从 ViewController 转换到 SecondViewController,我会指定我要移动的 subview 和我要搬到哪些地方:

extension ViewController: CustomTransitionOriginator {
var fromAnimatedSubviews: [UIView] { return [label] }
}

extension SecondViewController: CustomTransitionDestination {
var toAnimatedSubviews: [UIView] { return [label] }
}

为了支持解雇,我将添加相反的协议(protocol)一致性:

extension ViewController: CustomTransitionDestination {
var toAnimatedSubviews: [UIView] { return [label] }
}

extension SecondViewController: CustomTransitionOriginator {
var fromAnimatedSubviews: [UIView] { return [label] }
}

现在,我不想让您迷失在所有这些代码中,所以我建议您专注于高级设计(我在顶部列举的前七点)。但希望这足以让您遵循基本思想。

关于ios - View Controller Transition 动画 subview 位置,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46596481/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com