【发布时间】:2022-01-13 17:38:18
【问题描述】:
在使用UIPageViewController.TransitionStyle.scroll 时,是否可以将UIPageViewController 的动画方向反转为从右到左的语言?
左右滑动可以正确反转从右到左,但我的下一个按钮解决方案的动画方向错误(动画就像从左到右一样)。
我目前的解决方案是设置pageControl.semanticContentAttribute = .forceLeftToRight,并反转viewModels,以便从右到左的索引0是最后一个索引(viewModels.count - 1),以及反转动画方向+ viewControllerBefore/viewControllerAfter,例如:
-
viewControllerBefore变为:nextIndex = isRightToLeft ? vc.index - 1 : vc.index + 1(反之亦然,viewControllerAfter) -
nextTapped的inDirection参数为transitionFrom更改为isRightToLeft ? .reverse : .forward - 更改
loadFirstPage以将startIndex设置为numberOfPages - 1(又名viewModels.count - 1)而不是0 - 反转视图模型,使索引 0 为
viewModels.count - 1(在init中):if isRightToLeft { viewModels = viewModels.map({ viewModel in ViewModel(index: (numberOfPages - viewModel.index - 1), text: viewModel.text, color: viewModel.color) }).reversed()
但我想要一个简单地改变开箱即用动画方向的解决方案,就像在将spineLocation 更改为UIPageViewController.TransitionStyle.pageCurl 时似乎是可能的,比如this question 状态。
动画
请注意,对于How it is by default,页面来自右侧,但页面控制指示器向相反(预期的)方向移动。
默认实现:
import UIKit
struct ViewModel {
var index: Int
var text: String
var color: UIColor
}
class DummyViewController : UIViewController {
let label = UILabel()
let vm: ViewModel
let index: Int
init(vm: ViewModel) {
self.vm = vm
self.index = vm.index
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
override func loadView() {
let view = UIView()
view.backgroundColor = vm.color
let label = UILabel()
label.frame = CGRect(x: 20, y: 200, width: 200, height: 20)
label.textColor = .black
label.text = vm.text
view.addSubview(label)
self.view = view
}
}
class ViewController : UIViewController, UIPageViewControllerDelegate, UIPageViewControllerDataSource {
let pageViewController = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil)
let pageControl = UIPageControl()
let nextButton = UIButton()
var isRightToLeft: Bool {
traitCollection.layoutDirection == .rightToLeft
}
var currentIndex: Int = 0 {
didSet {
pageControl.currentPage = currentIndex
}
}
var numberOfPages: Int { return viewModels.count }
var viewModels: [ViewModel] = [
ViewModel(index: 0, text: "First", color: .red),
ViewModel(index: 1, text: "Second", color: .blue),
ViewModel(index: 2, text: "Third", color: .green),
]
required init?(coder: NSCoder) {
super.init(coder: coder)
pageViewController.dataSource = self
pageViewController.delegate = self
pageControl.currentPage = currentIndex
pageControl.numberOfPages = numberOfPages
nextButton.setTitle("Next", for: .normal)
nextButton.setTitleColor(.black, for: .normal)
}
override func viewDidLoad() {
addChild(pageViewController)
pageViewController.didMove(toParent: self)
view.addSubview(pageViewController.view)
view.addSubview(pageControl)
view.addSubview(nextButton)
view.subviews.forEach { $0.translatesAutoresizingMaskIntoConstraints = false }
NSLayoutConstraint.activate([
pageViewController.view.topAnchor.constraint(equalTo: view.topAnchor),
pageViewController.view.leftAnchor.constraint(equalTo: view.leftAnchor),
pageViewController.view.rightAnchor.constraint(equalTo: view.rightAnchor),
pageViewController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor),
pageControl.centerXAnchor.constraint(equalTo: view.centerXAnchor),
pageControl.centerYAnchor.constraint(equalTo: nextButton.centerYAnchor),
nextButton.trailingAnchor.constraint(equalTo: view.trailingAnchor),
nextButton.bottomAnchor.constraint(equalTo: view.layoutMarginsGuide.bottomAnchor)
])
nextButton.addTarget(self, action: #selector(nextTapped), for: .primaryActionTriggered)
loadFirstPage()
}
func loadFirstPage() {
let startIndex: Int
startIndex = 0
guard let start = viewControllerAtIndex(startIndex) else {
return
}
pageViewController.setViewControllers([start], direction: .forward, animated: true, completion: nil)
}
func transitionFrom(index: Int, inDirection direction: UIPageViewController.NavigationDirection) {
let nextIndex = direction == .forward ? index + 1 : index - 1
guard let next = viewControllerAtIndex(nextIndex) else {
return
}
pageViewController.setViewControllers([next], direction: direction, animated: true, completion: { finished in
self.pageViewController(self.pageViewController, didFinishAnimating: finished, previousViewControllers: [], transitionCompleted: finished)
})
}
func viewControllerAtIndex(_ index: Int) -> DummyViewController? {
guard index >= 0 && index < numberOfPages else { return nil }
let viewModel = viewModels[index]
return DummyViewController(vm: viewModel)
}
@objc
func nextTapped(_ sender: UIButton) {
guard currentIndex < numberOfPages - 1 else {
print("ending because \(currentIndex)")
return
}
let direction: UIPageViewController.NavigationDirection = .forward
transitionFrom(index: currentIndex, inDirection: direction)
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let vc = viewController as? DummyViewController else { return nil }
let nextIndex: Int = vc.index - 1
return viewControllerAtIndex(nextIndex)
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let vc = viewController as? DummyViewController else { return nil }
let nextIndex: Int = vc.index + 1
return viewControllerAtIndex(nextIndex)
}
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
guard let viewController = pageViewController.viewControllers?.first as? DummyViewController else { return }
currentIndex = viewController.index // Update currentIndex, which updates pageControl.currentPage
}
}
手动反转以支持从右到左(我试图避免的解决方案)。
排除了ViewModel 和DummyViewController,因为它们没有改变。
import UIKit
class ViewController : UIViewController, UIPageViewControllerDelegate, UIPageViewControllerDataSource {
let pageViewController = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil)
let pageControl = UIPageControl()
let nextButton = UIButton()
var isRightToLeft: Bool {
traitCollection.layoutDirection == .rightToLeft
}
var currentIndex: Int = 0 {
didSet {
pageControl.currentPage = currentIndex
}
}
var numberOfPages: Int {
return viewModels.count
}
var viewModels: [ViewModel] = [
ViewModel(index: 0, text: "First", color: .red),
ViewModel(index: 1, text: "Second", color: .blue),
ViewModel(index: 2, text: "Third", color: .green),
]
required init?(coder: NSCoder) {
super.init(coder: coder)
pageViewController.dataSource = self
pageViewController.delegate = self
pageControl.currentPage = currentIndex
pageControl.numberOfPages = numberOfPages
pageControl.semanticContentAttribute = .forceLeftToRight
nextButton.setTitle("Next", for: .normal)
nextButton.setTitleColor(.black, for: .normal)
if isRightToLeft { // HERE
viewModels = viewModels.map({ viewModel in
ViewModel(index: (numberOfPages - viewModel.index - 1), text: viewModel.text, color: viewModel.color)
}).reversed()
}
}
override func viewDidLoad() {
addChild(pageViewController)
pageViewController.didMove(toParent: self)
view.addSubview(pageViewController.view)
view.addSubview(pageControl)
view.addSubview(nextButton)
view.subviews.forEach { $0.translatesAutoresizingMaskIntoConstraints = false }
NSLayoutConstraint.activate([
pageViewController.view.topAnchor.constraint(equalTo: view.topAnchor),
pageViewController.view.leftAnchor.constraint(equalTo: view.leftAnchor),
pageViewController.view.rightAnchor.constraint(equalTo: view.rightAnchor),
pageViewController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor),
pageControl.centerXAnchor.constraint(equalTo: view.centerXAnchor),
pageControl.centerYAnchor.constraint(equalTo: nextButton.centerYAnchor),
nextButton.trailingAnchor.constraint(equalTo: view.trailingAnchor),
nextButton.bottomAnchor.constraint(equalTo: view.layoutMarginsGuide.bottomAnchor)
])
nextButton.addTarget(self, action: #selector(nextTapped), for: .primaryActionTriggered)
loadFirstPage()
}
func loadFirstPage() {
let startIndex: Int
if isRightToLeft { // HERE
startIndex = numberOfPages - 1
currentIndex = startIndex
} else {
startIndex = 0
}
guard let start = viewControllerAtIndex(startIndex) else {
return
}
pageViewController.setViewControllers([start], direction: .forward, animated: true, completion: nil)
}
func transitionFrom(index: Int, inDirection direction: UIPageViewController.NavigationDirection) { // UNCHANGED
let nextIndex = direction == .forward ? index + 1 : index - 1
guard let next = viewControllerAtIndex(nextIndex) else {
return
}
pageViewController.setViewControllers([next], direction: direction, animated: true, completion: { finished in
self.pageViewController(self.pageViewController, didFinishAnimating: finished, previousViewControllers: [], transitionCompleted: finished)
})
}
func viewControllerAtIndex(_ index: Int) -> DummyViewController? {
guard index >= 0 && index < numberOfPages else { return nil }
let viewModel = viewModels[index]
return DummyViewController(vm: vm)
}
@objc
func nextTapped(_ sender: UIButton) {
guard (currentIndex < numberOfPages - 1 && !isRightToLeft) || (currentIndex > 0 && isRightToLeft) else { // HERE
return
}
let direction: UIPageViewController.NavigationDirection = isRightToLeft ? .reverse : .forward // HERE
transitionFrom(index: currentIndex, inDirection: direction)
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let vc = viewController as? DummyViewController else {
return nil
}
let nextIndex: Int
if isRightToLeft { // HERE
nextIndex = vc.index + 1
} else {
nextIndex = vc.index - 1
}
return viewControllerAtIndex(nextIndex)
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let vc = viewController as? DummyViewController else {
return nil
}
let nextIndex: Int
if isRightToLeft { // HERE
nextIndex = vc.index - 1
} else {
nextIndex = vc.index + 1
}
return viewControllerAtIndex(nextIndex)
}
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
guard let viewController = pageViewController.viewControllers?.first as? DummyViewController else {
return
}
currentIndex = viewController.index
}
}
【问题讨论】:
标签: ios swift uipageviewcontroller right-to-left