我们设置了左右按钮,方便用户快速浏览不同的汽车。如果用户快速点击下一页10次或更多次,我们的页面视图控制器将丢失视图控制器。
这是车辆正确显示的车辆页面(模糊以隐藏不相关的信息)。见图:
如果滚动动画打开(true),则在快速点击右箭头6次或更多次后会丢失车辆页面。见图:
代码:
private func show(viewController:UIViewController, going direction: UIPageViewControllerNavigationDirection) {
let viewControllers = [viewController]
let isAnimated = true // false always works. However, animation is required.
setViewControllers(viewControllers, direction: direction, animated: isAnimated, completion: nil)
}
在调试时以及当页面视图控制器停止显示汽车时,我确保正在设置的视图控制器不是零并且列表(汽车)也是非零。
我尝试了UIPageViewController, how do I correctly jump to a specific page without messing up the order specified by the data source?的解决方案的变体,其中使用了完成块。但是,它没有用。
weak var pvcw: UIPageViewController? = self
setViewControllers(viewControllers, direction: direction, animated: true, completion: {(_ finished: Bool) -> Void in
let pvcs: UIPageViewController? = pvcw
if pvcs == nil {
return
}
DispatchQueue.main.async(execute: {() -> Void in
pvcs?.setViewControllers(viewControllers, direction: direction, animated: false) {(_ finished: Bool) -> Void in }
})
})
有什么想法吗?谢谢。
我注意到有时候所包含的View Controller可以偏离中心而不是完全缺失。
我更深入地了解了视图控制器完全丢失的情况。单击“调试视图层次结构”并打开“Show Clipped Content”时,视图控制器完全丢失时显示以下内容:
因此,似乎缺少的内容被剪裁/超出范围。
仅显示线框显示以下内容:
页面视图控制器有一个
我还看到,当事情很奇怪时,_UIQueuingScrollView的界限是完全不同的。当一切正常时,边界的x为1125,而X为375。
仅当使用滚动的过渡样式而不是页面卷曲时才会发生这种情况。使用Page Curl时,一切正常。
我们如何预防/解决此问题?
此代码使问题消失。然而,它留下了更刺耳的经历。也许由于延迟0.4秒,蓝色背景有时会显示正常使用。
private func show(viewController:UIViewController, going direction: UIPageViewControllerNavigationDirection) {
let viewControllers = [viewController]
setViewControllers(viewControllers, direction: direction, animated: true, completion: { (_) in
DispatchQueue.main.asyncAfter(deadline: .now() + 0.4, execute: {
self.setViewControllers(viewControllers, direction: direction, animated: false, completion: nil)
})
})
}
这不是一个好的用户体验。 有更好的方法吗?
我希望滚动过渡平滑,不要简单地显示蓝色背景,也不要丢失其内容,即View Controller内容。
答案 0 :(得分:2)
虽然真正的答案是让视觉控制器尽可能简单(但不是更简单),但这里是修复问题的代码,当用户导航到下一个视图时,有时会显示背景的副作用控制器。
private func show(viewController:UIViewController, going direction: UIPageViewControllerNavigationDirection) {
let viewControllers = [viewController]
setViewControllers(viewControllers, direction: direction, animated: true, completion: { (_) in
DispatchQueue.main.asyncAfter(deadline: .now() + 0.4, execute: {
self.setViewControllers(viewControllers, direction: direction, animated: false, completion: nil)
})
})
}
答案 1 :(得分:1)
一个简单的解决方案是通过添加一个小的"提前点击来解除按钮水龙头与视图控制器的变化。缓冲。创建一个按钮队列(使用一个简单的NSMutableArray
充当FIFO队列),在其中添加每个导航按钮,然后在添加之前调用队列为空的出队函数。
在dequeue函数中,删除第一个条目并相应地更改视图,然后在setViewControllers
完成处理程序中再次调用自身,如果队列不为空。
确保仅在主线程上进行处理以避免线程问题。如果您愿意,您还可以添加限制数量"提前点击"你允许,也许可以在方向性变化上刷新队列。
答案 2 :(得分:1)
您好我已经创建了Sample Project,这可以解决您的问题。我添加了100个ViewControllers(通过循环),它可以正常使用滚动动画。事情就在他们的位置。
我在这个项目中所做的是:
为具有两个属性的页面创建了一个BaseClass
a)Int
类型的pageIndexb)委托回调协议
通过ContainerView
创建了一个名为Page的ViewController,它扩展了PageViewBase
运行循环计数100并将数据添加到数组并设置数据源并委托给self(PageControlelr)并根据pageIndex属性进行管理
PageViewController
class PageViewController: UIPageViewController {
var list = [Page]()
var sb: UIStoryboard?
var viewController: ViewController! // settting from ViewController
override func viewDidLoad() {
super.viewDidLoad()
sb = UIStoryboard(name: "Main", bundle: nil)
DispatchQueue.main.asyncAfter(deadline: .now()+0.4, execute: {
self.setupList()
})
}
func setupList(){
for i in 0..<100{
let model = PageModel(title: "Title \(i + 1)", subTitle: "SubTitle \(i + 1)")
let page = sb?.instantiateViewController(withIdentifier: "PageID") as! Page
page.data = model
page.pageIndex = i
page.delegate = viewController
list.append(page)
}
self.delegate = self
self.dataSource = self
setViewControllers([list[0]], direction: .forward, animated: true, completion: nil)
self.updateCurrentPageLabel(index: 0)
}
func movePage(index: Int){
let currentIndex = self.viewControllers![0] as! Page
self.updateCurrentPageLabel(index: index)
setViewControllers([list[index]], direction: index > currentIndex.pageIndex ? .forward : .reverse, animated: true)
}
func getCurrentPageIndex() -> Int{
return (self.viewControllers![0] as! Page).pageIndex
}
func updateCurrentPageLabel(index: Int){
(self.parent as? ViewController)?.currentListingLabel.text = "\(index + 1) of \(list.count)"
}
}
extension PageViewController: UIPageViewControllerDelegate{
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
let currentIndex = (self.viewControllers![0] as! Page).pageIndex
self.updateCurrentPageLabel(index: currentIndex)
}
}
extension PageViewController: UIPageViewControllerDataSource{
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
let index = (viewController as! Page).pageIndex
if index > 0 {
return list[index-1]
}
return nil
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
let index = (viewController as! Page).pageIndex
if index < list.count-1 {
return list[index+1]
}
return nil
}
}
页
import UIKit
struct PageModel {
var title: String
var subTitle: String
}
class Page: PageViewBase {
@IBOutlet weak var titleLabel: UILabel!
@IBOutlet weak var subTitleLabel: UILabel!
@IBOutlet weak var imageView: UIImageView!
@IBOutlet weak var btnWorking: UIButton!
var data: PageModel?
override func viewDidLoad() {
super.viewDidLoad()
setupTags()
setupActions()
setupData()
}
func setupData(){
if let data = data{
self.titleLabel.text = data.title
self.subTitleLabel.text = data.subTitle
imageView.image = #imageLiteral(resourceName: "car")
}
}
enum buttonTags: Int{
case working = 1
}
func setupTags(){
btnWorking.tag = buttonTags.working.rawValue
}
func setupActions(){
btnWorking.addTarget(self, action: #selector(self.didSelect(_:)), for: .touchUpInside)
}
@objc func didSelect(_ sender: UIView){
if let tag = buttonTags.init(rawValue: sender.tag){
switch tag{
case .working:
delegate?.didReceive(withMessage: "wokring button clicked of index \(pageIndex)")
}
}
}
}
ViewController // MainController
import UIKit
protocol CallBack {
func didReceive(withMessage message: String)
}
class ViewController: UIViewController {
@IBOutlet weak var containerView: UIView!
@IBOutlet weak var btnCall: UIButton!
@IBOutlet weak var btnMessage: UIButton!
@IBOutlet weak var btnNext: UIButton!
@IBOutlet weak var btnBack: UIButton!
@IBOutlet weak var currentListingLabel: UILabel!
var pageController: PageViewController?
override func viewDidLoad() {
super.viewDidLoad()
setupTags()
setupActions()
setupContainerView()
}
enum buttonTags: Int{
case call = 1
case message
case next
case back
}
func setupTags(){
btnCall.tag = buttonTags.call.rawValue
btnMessage.tag = buttonTags.message.rawValue
btnNext.tag = buttonTags.next.rawValue
btnBack.tag = buttonTags.back.rawValue
}
func setupActions(){
btnCall.addTarget(self, action: #selector(self.didSelect(_:)), for: .touchUpInside)
btnMessage.addTarget(self, action: #selector(self.didSelect(_:)), for: .touchUpInside)
btnNext.addTarget(self, action: #selector(self.didSelect(_:)), for: .touchUpInside)
btnBack.addTarget(self, action: #selector(self.didSelect(_:)), for: .touchUpInside)
}
@objc func didSelect(_ sender: UIView){
if let tag = buttonTags.init(rawValue: sender.tag){
switch tag{
case .call:
print("Call button called for index \(pageController?.getCurrentPageIndex() ?? 0)")
case .message:
print("message button called for index \(pageController?.getCurrentPageIndex() ?? 0)")
case .next:
if let p = pageController{
let currentIndex = p.getCurrentPageIndex()
if currentIndex < p.list.count - 1{
p.movePage(index: currentIndex + 1)
}
}
case .back:
if let p = pageController{
let currentIndex = p.getCurrentPageIndex()
if currentIndex > 0{
p.movePage(index: currentIndex - 1)
}
}
}
}
}
func setupContainerView(){
let sb = UIStoryboard(name: "Main", bundle: nil)
pageController = sb.instantiateViewController(withIdentifier: "PageViewControllerID") as? PageViewController
pageController?.viewController = self
addViewIntoParentViewController(vc: pageController)
}
func addViewIntoParentViewController(vc: UIViewController?){
if let vc = vc{
for v in self.containerView.subviews{
v.removeFromSuperview()
}
self.containerView.addSubview(vc.view)
self.containerView.translatesAutoresizingMaskIntoConstraints = false
vc.view.translatesAutoresizingMaskIntoConstraints = false
addChildViewController(vc)
NSLayoutConstraint.activate([
vc.view.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
vc.view.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
vc.view.topAnchor.constraint(equalTo: containerView.topAnchor),
vc.view.bottomAnchor.constraint(equalTo: containerView.bottomAnchor)
])
vc.didMove(toParentViewController: self)
}
}
}
extension ViewController: CallBack{
func didReceive(withMessage message: String) {
print("message: \(message)")
}
}
PageViewBase
import UIKit
class PageViewBase: UIViewController {
var pageIndex = -1
var delegate: CallBack?
}