我试图实现的效果:链接到视频(请注意,在此视频中存在问题)。
基本上,X 轴上同时进行翻页和平移(由于视频是横向注册的,因此平移出现在 y 轴上)。
每当调用
setViewControllers
的 UIPageViewController
方法时,都会在后台执行卷页动画,而翻译是通过更新约束通过 UIView.animate
实现的。
在 iOS 15 之前,这一切都工作得很好,但是自 iOS 16 以来,动画很滞后并且无法按预期工作,请参见下文。
该问题显然与执行卷曲动画时发生的 x 平移有关,因为如果没有它,一切都会正常工作。我认为这可能不是执行多个动画的正确方法,但是由于页面卷曲发生在幕后,我不太确定如何处理它。
我的问题是:如何同时实现翻页和x平移动画?顺便说一句,有谁知道为什么我遇到的奇怪行为只发生在较新版本的 iOS 上?
编辑:为了进一步澄清我的意思,this是一个curl+x翻译的慢动作视频,其中问题是可见的。 vc中用红线突出显示的部分不应该是透明的,而应该是红色的。这是一个bug。
这个是没有x平移的curl动画,其中没有透明度问题/没有bug。
重现问题的示例项目 - 请注意,它的目的是仅在横向工作:
import UIKit
final class ViewController: UIViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate {
private var pageController: UIPageViewController?
private var pageControllerViewLeadingAnchor: NSLayoutConstraint?
override func viewDidLoad() {
super.viewDidLoad()
// init
pageController = UIPageViewController(transitionStyle: .pageCurl, navigationOrientation: .horizontal, options: nil)
pageController?.dataSource = self
pageController?.delegate = self
// adding it
addChild(pageController!)
view.addSubview(pageController!.view)
// constraints
pageController?.view.translatesAutoresizingMaskIntoConstraints = false
pageController?.view.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
pageController?.view.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
pageControllerViewLeadingAnchor = pageController?.view.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: -view.bounds.width/4)
pageControllerViewLeadingAnchor?.isActive = true
pageController?.view.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
DispatchQueue.main.asyncAfter(deadline: .now()+0.5) {
// x translation animation
UIView.animate(withDuration: 0.3, animations: {
self.pageControllerViewLeadingAnchor?.constant = 0
self.view.layoutIfNeeded()
})
// pageController curl animation
let vc = UIViewController()
let vc2 = UIViewController()
vc.view.backgroundColor = .red
vc2.view.backgroundColor = .blue
self.pageController?.setViewControllers([vc, vc2], direction: .forward, animated: true, completion: nil)
}
}
func pageViewController(_ pageViewController: UIPageViewController, spineLocationFor orientation: UIInterfaceOrientation) -> UIPageViewController.SpineLocation {
// setting spine to mid and adding vcs
let vc = UIViewController()
let vc2 = UIViewController()
vc2.view.backgroundColor = .green
pageController?.setViewControllers([vc, vc2], direction: .forward, animated: true, completion: nil)
pageController?.isDoubleSided = true
return .mid
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
return UIViewController()
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
return UIViewController()
}
}
您可能想尝试将页面控制器嵌入到“容器”中
UIView
...然后将容器动画到位。
尝试一下:
final class ViewController: UIViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate {
private var pageController: UIPageViewController?
private var containerViewLeadingAnchor: NSLayoutConstraint?
private let pageControllerContainerView = UIView()
override func viewDidLoad() {
super.viewDidLoad()
// init
pageController = UIPageViewController(transitionStyle: .pageCurl, navigationOrientation: .horizontal, options: nil)
pageController?.dataSource = self
pageController?.delegate = self
// adding it
addChild(pageController!)
// add the container view
pageControllerContainerView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(pageControllerContainerView)
// add the page controller view as a subview of theh container view
pageControllerContainerView.addSubview(pageController!.view)
// constrain page controller view to all 4 sides of the container view
pageController?.view.translatesAutoresizingMaskIntoConstraints = false
pageController?.view.topAnchor.constraint(equalTo: pageControllerContainerView.topAnchor).isActive = true
pageController?.view.bottomAnchor.constraint(equalTo: pageControllerContainerView.bottomAnchor).isActive = true
pageController?.view.leadingAnchor.constraint(equalTo: pageControllerContainerView.leadingAnchor).isActive = true
pageController?.view.trailingAnchor.constraint(equalTo: pageControllerContainerView.trailingAnchor).isActive = true
guard let g = view else { return }
NSLayoutConstraint.activate([
// constrain container view Top / Bottom / Width
pageControllerContainerView.topAnchor.constraint(equalTo: g.topAnchor),
pageControllerContainerView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: 0.0),
pageControllerContainerView.widthAnchor.constraint(equalTo: g.widthAnchor, multiplier: 1.0),
])
// container view leading constant will be set in viewDidLayoutSubviews()
containerViewLeadingAnchor = pageControllerContainerView.leadingAnchor.constraint(equalTo: g.leadingAnchor)
containerViewLeadingAnchor?.isActive = true
pageControllerContainerView.clipsToBounds = true
}
// track the container width
var cvw: CGFloat = -1
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// only execute if the container view frame width has changed
// (such as on launch)
if cvw != pageControllerContainerView.frame.width {
cvw = pageControllerContainerView.frame.width
containerViewLeadingAnchor?.constant = -cvw * 0.25
}
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
DispatchQueue.main.asyncAfter(deadline: .now()+1.5) {
// pageController curl animation
let vc = UIViewController()
let vc2 = UIViewController()
vc.view.backgroundColor = .red
vc2.view.backgroundColor = .blue
self.pageController?.setViewControllers([vc, vc2], direction: .forward, animated: true, completion: nil)
// x translation animation
UIView.animate(withDuration: 0.3, animations: {
self.containerViewLeadingAnchor?.constant = 0
self.view.layoutIfNeeded()
})
}
}
func pageViewController(_ pageViewController: UIPageViewController, spineLocationFor orientation: UIInterfaceOrientation) -> UIPageViewController.SpineLocation {
// setting spine to mid and adding vcs
let vc = UIViewController()
let vc2 = UIViewController()
vc2.view.backgroundColor = .green
pageController?.setViewControllers([vc, vc2], direction: .forward, animated: true, completion: nil)
pageController?.isDoubleSided = true
return .mid
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
return UIViewController()
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
return UIViewController()
}
}
编辑
添加
pageControllerContainerView.clipsToBounds = true
出现 来解决问题。