设置后设置视图控制器时,以编程方式添加的页面控件消失
Programmatically-added page control disappears when setting view controllers after setup
问题
点击页面 i
上的跳过按钮(调用 setViewControllers(_:animated:)
并将用户转到页面视图控制器中的最后一页),然后滑动回到页面 i
再次,页面控件消失。
想要的结果
当所述页面视图控制器包含不同类型的视图控制器时,我想以编程方式在页面视图控制器中的每个视图控制器的底部添加并显示自定义页面控件。
目前为解决问题所做的努力
- 将页面控件添加到基本视图控制器每次出现。
- 在包含缺失页面控件的视图控制器上调用
loadView()
。
代码
我有一个 WalkthroughRootViewController
,其中包含一个 UIPageViewController
。页面视图控制器中的视图控制器类型是类型 WalkthroughBaseViewController
的两个子类,第一个 n-1
是一个类型,最后一个是另一个。我没有包括最后一种类型的代码,因为据我所知它正在工作。
我在 WalkthroughBaseViewController
中有此代码:
lazy var pageControl: UIPageControl = {
let pageControl = UIPageControl(frame: .zero)
pageControl.translatesAutoresizingMaskIntoConstraints = false
pageControl.numberOfPages = numberOfPages
pageControl.sizeToFit()
pageControl.pageIndicatorTintColor = Colors.brown
pageControl.currentPageIndicatorTintColor = Colors.silver
pageControl.isUserInteractionEnabled = false
pageControl.isEnabled = false
return pageControl
}()
页面控件添加到viewDidLoad()
中的视图:
view.addSubview(pageControl)
NSLayoutConstraint.activate([
pageControl.bottomAnchor.constraint(equalTo: view.bottomAnchor),
pageControl.centerXAnchor.constraint(equalTo: view.centerXAnchor)
])
如果用户在第一个 n-1
视图控制器中的任何一个上,则有一个跳过按钮,用户可以点击以跳到最后一个视图控制器。代码是
func skipWalkthrough() {
guard let viewController = walkthroughPageViewControllerDataSource.viewController(at: lastIndex, storyboard: storyboard!) else { return }
walkthroughPageViewController.setViewControllers([viewController], direction: .forward, animated: true)
}
参考
我已突出显示我认为重要的代码,但这里是与应用程序演练相关的所有文件。
WalkthroughRootViewController
import UIKit
class WalkthroughRootViewController: UIViewController {
// MARK: Regular Properties
var walkthroughPageViewController: UIPageViewController!
var walkthroughImages = [
Images.w1,
Images.w2
]
var walkthroughStrings: [String] = [
.localized(.walkthroughTitle1),
.localized(.walkthroughZipCodeTitle)
]
// MARK: Lazy Properties
lazy var walkthroughPageViewControllerDataSource: WalkthroughPageViewControllerDataSource = {
var dataSource = WalkthroughPageViewControllerDataSource()
dataSource.walkthroughRootViewController = self
return dataSource
}()
// MARK: Computed Properties
var lastIndex: Int {
return walkthroughImages.count - 1
}
var temporaryUserInput: String?
var temporarySwitchPosition = false
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
}
// MARK: View Controller Life Cycle
extension WalkthroughRootViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Stop gray shadow from appearing under transition.
navigationController?.view.backgroundColor = .white
configurePageViewController()
}
}
// MARK: Helper Methods
extension WalkthroughRootViewController {
func configurePageViewController() {
walkthroughPageViewController = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil)
walkthroughPageViewController.dataSource = walkthroughPageViewControllerDataSource
walkthroughPageViewController.delegate = walkthroughPageViewControllerDataSource
let startingViewController = storyboard!.instantiateViewController(withIdentifier: Strings.ViewControllerIdentifiers.walkthroughImage) as! WalkthroughImageViewController
let startIndex = 0
startingViewController.delegate = self
startingViewController.pageIndex = startIndex
startingViewController.text = walkthroughStrings[startIndex]
startingViewController.image = walkthroughImages[startIndex]
startingViewController.numberOfPages = walkthroughImages.count
walkthroughPageViewController.setViewControllers([startingViewController], direction: .forward, animated: true)
walkthroughPageViewController.view.frame = view.bounds
add(walkthroughPageViewController)
}
}
extension WalkthroughRootViewController: WalkthroughDelegate {
func skipWalkthrough() {
guard let viewController = walkthroughPageViewControllerDataSource.viewController(at: lastIndex, storyboard: storyboard!) else { return }
walkthroughPageViewController.setViewControllers([viewController], direction: .forward, animated: true)
}
}
extension WalkthroughRootViewController: WalkthrouZipCodeViewControllerDelegate {
func walkththroughZipCodeViewController(_ viewController: WalkthroughZipCodeViewController, userEnteredText enteredText: String) {
temporaryUserInput = enteredText
}
func walkthroughZipCodeViewController(_ viewController: WalkthroughZipCodeViewController, userChangedSwitchPosition position: Bool) {
temporarySwitchPosition = position
}
}
WalkthroughBaseViewController
import UIKit
protocol WalkthroughDelegate: class {
func skipWalkthrough()
}
class WalkthroughBaseViewController: UIViewController {
// MARK: Regular Properties
var pageIndex = 0
var text = ""
var delegate: WalkthroughDelegate?
var numberOfPages = 0
// Lazy Properties
lazy var pageControl: UIPageControl = {
let pageControl = UIPageControl(frame: .zero)
pageControl.translatesAutoresizingMaskIntoConstraints = false
pageControl.numberOfPages = numberOfPages
pageControl.sizeToFit()
pageControl.pageIndicatorTintColor = Colors.brown
pageControl.currentPageIndicatorTintColor = Colors.silver
pageControl.isUserInteractionEnabled = false
pageControl.isEnabled = false
return pageControl
}()
}
// MARK: View Controller Life Cycle
extension WalkthroughBaseViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = Colors.silver
view.addSubview(pageControl)
NSLayoutConstraint.activate([
pageControl.bottomAnchor.constraint(equalTo: view.bottomAnchor),
pageControl.centerXAnchor.constraint(equalTo: view.centerXAnchor)
])
view.accessibilityIdentifier = Strings.AccessibilityIdentifiers.walkthrough
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
pageControl.currentPage = pageIndex
}
}
WalkthroughImageViewController
import UIKit
class WalkthroughImageViewController: WalkthroughBaseViewController {
// MARK: @IBOutlets
@IBOutlet weak var titleLabel: UILabel! {
didSet {
titleLabel.adjustsFontSizeToFitWidth = true
titleLabel.textColor = Colors.silver
titleLabel.numberOfLines = 0
}
}
@IBOutlet weak var skipWalkthroughButton: UIButton! {
didSet {
skipWalkthroughButton.setTitleColor(Colors.silver, for: .normal)
skipWalkthroughButton.titleLabel?.font = UIFont.preferredBoldFont(for: .body)
skipWalkthroughButton.setTitle(.localized(.skip), for: .normal)
}
}
@IBOutlet weak var imageView: UIImageView! {
didSet {
imageView.layer.shadowColor = Colors.brown.cgColor
imageView.layer.shadowOffset = CGSize(width: 0, height: 1)
imageView.layer.shadowOpacity = 1
imageView.layer.shadowRadius = 1.0
imageView.clipsToBounds = false
imageView.contentMode = .scaleAspectFill
}
}
// MARK: Regular Properties
var image: UIImage?
// MARK: View Controller Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
imageView.image = image
titleLabel.text = text
}
}
// MARK: @IBActions
extension WalkthroughImageViewController {
@IBAction func skipWalkthrough(_ sender: UIButton) {
delegate?.skipWalkthrough()
}
}
WalkthroughPageViewControllerDataSource
import UIKit
class WalkthroughPageViewControllerDataSource: NSObject {
// MARK: Regular Properties
var walkthroughRootViewController: WalkthroughRootViewController!
}
extension WalkthroughPageViewControllerDataSource: UIPageViewControllerDataSource {
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
var index = indexOfViewController(viewController as! WalkthroughBaseViewController)
if index == NSNotFound || index == 0 {
return nil
}
index -= 1
return self.viewController(at: index, storyboard: walkthroughRootViewController.storyboard!)
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
var index = indexOfViewController(viewController as! WalkthroughBaseViewController)
if index == NSNotFound {
return nil
}
index += 1
if index == walkthroughRootViewController.walkthroughImages.count {
return nil
}
return self.viewController(at: index, storyboard: walkthroughRootViewController.storyboard!)
}
}
extension WalkthroughPageViewControllerDataSource {
func viewController(at index: Int, storyboard: UIStoryboard) -> WalkthroughBaseViewController? {
if walkthroughRootViewController.walkthroughImages.count == 0 || index >= walkthroughRootViewController.walkthroughImages.count {
return nil
}
var viewController: WalkthroughBaseViewController?
if index == walkthroughRootViewController.lastIndex {
viewController = storyboard.instantiateViewController(withIdentifier: Strings.ViewControllerIdentifiers.walkthroughZipCode) as? WalkthroughZipCodeViewController
if let viewController = viewController as? WalkthroughZipCodeViewController {
viewController.pageIndex = index
viewController.walkthroughZipCodeDelegate = walkthroughRootViewController
viewController.temporaryUserInput = walkthroughRootViewController.temporaryUserInput
viewController.temporarySwitchPosition = walkthroughRootViewController.temporarySwitchPosition
viewController.numberOfPages = walkthroughRootViewController.walkthroughImages.count
viewController.image = walkthroughRootViewController.walkthroughImages[index]
}
} else {
viewController = storyboard.instantiateViewController(withIdentifier: Strings.ViewControllerIdentifiers.walkthroughImage) as? WalkthroughImageViewController
if let viewController = viewController as? WalkthroughImageViewController {
viewController.delegate = walkthroughRootViewController
viewController.pageIndex = index
viewController.image = walkthroughRootViewController.walkthroughImages[index]
viewController.text = walkthroughRootViewController.walkthroughStrings[index]
}
}
return viewController
}
func indexOfViewController(_ viewController: WalkthroughBaseViewController) -> Int {
return viewController.pageIndex
}
}
extension WalkthroughPageViewControllerDataSource: UIPageViewControllerDelegate {
}
创建一个 UIPageControl
放入 WalkthroughRootViewController 并在导航页面时更新它 - 不要为每个子项创建一个页面控件。
尽量不要使用 extension
s 覆盖方法 - 这会给您带来麻烦 - 请参阅 this blog entry。
问题
点击页面 i
上的跳过按钮(调用 setViewControllers(_:animated:)
并将用户转到页面视图控制器中的最后一页),然后滑动回到页面 i
再次,页面控件消失。
想要的结果
当所述页面视图控制器包含不同类型的视图控制器时,我想以编程方式在页面视图控制器中的每个视图控制器的底部添加并显示自定义页面控件。
目前为解决问题所做的努力
- 将页面控件添加到基本视图控制器每次出现。
- 在包含缺失页面控件的视图控制器上调用
loadView()
。
代码
我有一个 WalkthroughRootViewController
,其中包含一个 UIPageViewController
。页面视图控制器中的视图控制器类型是类型 WalkthroughBaseViewController
的两个子类,第一个 n-1
是一个类型,最后一个是另一个。我没有包括最后一种类型的代码,因为据我所知它正在工作。
我在 WalkthroughBaseViewController
中有此代码:
lazy var pageControl: UIPageControl = {
let pageControl = UIPageControl(frame: .zero)
pageControl.translatesAutoresizingMaskIntoConstraints = false
pageControl.numberOfPages = numberOfPages
pageControl.sizeToFit()
pageControl.pageIndicatorTintColor = Colors.brown
pageControl.currentPageIndicatorTintColor = Colors.silver
pageControl.isUserInteractionEnabled = false
pageControl.isEnabled = false
return pageControl
}()
页面控件添加到viewDidLoad()
中的视图:
view.addSubview(pageControl)
NSLayoutConstraint.activate([
pageControl.bottomAnchor.constraint(equalTo: view.bottomAnchor),
pageControl.centerXAnchor.constraint(equalTo: view.centerXAnchor)
])
如果用户在第一个 n-1
视图控制器中的任何一个上,则有一个跳过按钮,用户可以点击以跳到最后一个视图控制器。代码是
func skipWalkthrough() {
guard let viewController = walkthroughPageViewControllerDataSource.viewController(at: lastIndex, storyboard: storyboard!) else { return }
walkthroughPageViewController.setViewControllers([viewController], direction: .forward, animated: true)
}
参考
我已突出显示我认为重要的代码,但这里是与应用程序演练相关的所有文件。
WalkthroughRootViewController
import UIKit
class WalkthroughRootViewController: UIViewController {
// MARK: Regular Properties
var walkthroughPageViewController: UIPageViewController!
var walkthroughImages = [
Images.w1,
Images.w2
]
var walkthroughStrings: [String] = [
.localized(.walkthroughTitle1),
.localized(.walkthroughZipCodeTitle)
]
// MARK: Lazy Properties
lazy var walkthroughPageViewControllerDataSource: WalkthroughPageViewControllerDataSource = {
var dataSource = WalkthroughPageViewControllerDataSource()
dataSource.walkthroughRootViewController = self
return dataSource
}()
// MARK: Computed Properties
var lastIndex: Int {
return walkthroughImages.count - 1
}
var temporaryUserInput: String?
var temporarySwitchPosition = false
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
}
// MARK: View Controller Life Cycle
extension WalkthroughRootViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Stop gray shadow from appearing under transition.
navigationController?.view.backgroundColor = .white
configurePageViewController()
}
}
// MARK: Helper Methods
extension WalkthroughRootViewController {
func configurePageViewController() {
walkthroughPageViewController = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil)
walkthroughPageViewController.dataSource = walkthroughPageViewControllerDataSource
walkthroughPageViewController.delegate = walkthroughPageViewControllerDataSource
let startingViewController = storyboard!.instantiateViewController(withIdentifier: Strings.ViewControllerIdentifiers.walkthroughImage) as! WalkthroughImageViewController
let startIndex = 0
startingViewController.delegate = self
startingViewController.pageIndex = startIndex
startingViewController.text = walkthroughStrings[startIndex]
startingViewController.image = walkthroughImages[startIndex]
startingViewController.numberOfPages = walkthroughImages.count
walkthroughPageViewController.setViewControllers([startingViewController], direction: .forward, animated: true)
walkthroughPageViewController.view.frame = view.bounds
add(walkthroughPageViewController)
}
}
extension WalkthroughRootViewController: WalkthroughDelegate {
func skipWalkthrough() {
guard let viewController = walkthroughPageViewControllerDataSource.viewController(at: lastIndex, storyboard: storyboard!) else { return }
walkthroughPageViewController.setViewControllers([viewController], direction: .forward, animated: true)
}
}
extension WalkthroughRootViewController: WalkthrouZipCodeViewControllerDelegate {
func walkththroughZipCodeViewController(_ viewController: WalkthroughZipCodeViewController, userEnteredText enteredText: String) {
temporaryUserInput = enteredText
}
func walkthroughZipCodeViewController(_ viewController: WalkthroughZipCodeViewController, userChangedSwitchPosition position: Bool) {
temporarySwitchPosition = position
}
}
WalkthroughBaseViewController
import UIKit
protocol WalkthroughDelegate: class {
func skipWalkthrough()
}
class WalkthroughBaseViewController: UIViewController {
// MARK: Regular Properties
var pageIndex = 0
var text = ""
var delegate: WalkthroughDelegate?
var numberOfPages = 0
// Lazy Properties
lazy var pageControl: UIPageControl = {
let pageControl = UIPageControl(frame: .zero)
pageControl.translatesAutoresizingMaskIntoConstraints = false
pageControl.numberOfPages = numberOfPages
pageControl.sizeToFit()
pageControl.pageIndicatorTintColor = Colors.brown
pageControl.currentPageIndicatorTintColor = Colors.silver
pageControl.isUserInteractionEnabled = false
pageControl.isEnabled = false
return pageControl
}()
}
// MARK: View Controller Life Cycle
extension WalkthroughBaseViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = Colors.silver
view.addSubview(pageControl)
NSLayoutConstraint.activate([
pageControl.bottomAnchor.constraint(equalTo: view.bottomAnchor),
pageControl.centerXAnchor.constraint(equalTo: view.centerXAnchor)
])
view.accessibilityIdentifier = Strings.AccessibilityIdentifiers.walkthrough
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
pageControl.currentPage = pageIndex
}
}
WalkthroughImageViewController
import UIKit
class WalkthroughImageViewController: WalkthroughBaseViewController {
// MARK: @IBOutlets
@IBOutlet weak var titleLabel: UILabel! {
didSet {
titleLabel.adjustsFontSizeToFitWidth = true
titleLabel.textColor = Colors.silver
titleLabel.numberOfLines = 0
}
}
@IBOutlet weak var skipWalkthroughButton: UIButton! {
didSet {
skipWalkthroughButton.setTitleColor(Colors.silver, for: .normal)
skipWalkthroughButton.titleLabel?.font = UIFont.preferredBoldFont(for: .body)
skipWalkthroughButton.setTitle(.localized(.skip), for: .normal)
}
}
@IBOutlet weak var imageView: UIImageView! {
didSet {
imageView.layer.shadowColor = Colors.brown.cgColor
imageView.layer.shadowOffset = CGSize(width: 0, height: 1)
imageView.layer.shadowOpacity = 1
imageView.layer.shadowRadius = 1.0
imageView.clipsToBounds = false
imageView.contentMode = .scaleAspectFill
}
}
// MARK: Regular Properties
var image: UIImage?
// MARK: View Controller Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
imageView.image = image
titleLabel.text = text
}
}
// MARK: @IBActions
extension WalkthroughImageViewController {
@IBAction func skipWalkthrough(_ sender: UIButton) {
delegate?.skipWalkthrough()
}
}
WalkthroughPageViewControllerDataSource
import UIKit
class WalkthroughPageViewControllerDataSource: NSObject {
// MARK: Regular Properties
var walkthroughRootViewController: WalkthroughRootViewController!
}
extension WalkthroughPageViewControllerDataSource: UIPageViewControllerDataSource {
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
var index = indexOfViewController(viewController as! WalkthroughBaseViewController)
if index == NSNotFound || index == 0 {
return nil
}
index -= 1
return self.viewController(at: index, storyboard: walkthroughRootViewController.storyboard!)
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
var index = indexOfViewController(viewController as! WalkthroughBaseViewController)
if index == NSNotFound {
return nil
}
index += 1
if index == walkthroughRootViewController.walkthroughImages.count {
return nil
}
return self.viewController(at: index, storyboard: walkthroughRootViewController.storyboard!)
}
}
extension WalkthroughPageViewControllerDataSource {
func viewController(at index: Int, storyboard: UIStoryboard) -> WalkthroughBaseViewController? {
if walkthroughRootViewController.walkthroughImages.count == 0 || index >= walkthroughRootViewController.walkthroughImages.count {
return nil
}
var viewController: WalkthroughBaseViewController?
if index == walkthroughRootViewController.lastIndex {
viewController = storyboard.instantiateViewController(withIdentifier: Strings.ViewControllerIdentifiers.walkthroughZipCode) as? WalkthroughZipCodeViewController
if let viewController = viewController as? WalkthroughZipCodeViewController {
viewController.pageIndex = index
viewController.walkthroughZipCodeDelegate = walkthroughRootViewController
viewController.temporaryUserInput = walkthroughRootViewController.temporaryUserInput
viewController.temporarySwitchPosition = walkthroughRootViewController.temporarySwitchPosition
viewController.numberOfPages = walkthroughRootViewController.walkthroughImages.count
viewController.image = walkthroughRootViewController.walkthroughImages[index]
}
} else {
viewController = storyboard.instantiateViewController(withIdentifier: Strings.ViewControllerIdentifiers.walkthroughImage) as? WalkthroughImageViewController
if let viewController = viewController as? WalkthroughImageViewController {
viewController.delegate = walkthroughRootViewController
viewController.pageIndex = index
viewController.image = walkthroughRootViewController.walkthroughImages[index]
viewController.text = walkthroughRootViewController.walkthroughStrings[index]
}
}
return viewController
}
func indexOfViewController(_ viewController: WalkthroughBaseViewController) -> Int {
return viewController.pageIndex
}
}
extension WalkthroughPageViewControllerDataSource: UIPageViewControllerDelegate {
}
创建一个 UIPageControl
放入 WalkthroughRootViewController 并在导航页面时更新它 - 不要为每个子项创建一个页面控件。
尽量不要使用 extension
s 覆盖方法 - 这会给您带来麻烦 - 请参阅 this blog entry。