I have a problem with my PageViewController. It works as expected when I switch views by taping on the page indicators, but when I swipe every second view is blank (black).The preview while swiping is displayed properly, but when I complete the swipe the view disappears.
import UIKit
class ScheduleViewController: UIViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate {
#IBAction func editButton(sender: AnyObject) {
(self.pageViewController.view as! UITableView).setEditing(true, animated: true)
print((self.pageViewController.view as! UITableView).editing)
}
#IBOutlet weak var navBar: UINavigationItem!
var pageViewController: UIPageViewController!
var index = 0
var viewControllers: [ContentViewController] = []
override func viewDidLoad()
{
self.navigationController!.navigationBar.translucent = false
self.tabBarController!.tabBar.translucent = false
self.pageViewController = self.storyboard?.instantiateViewControllerWithIdentifier("ScheduleViewControllerPage") as! UIPageViewController
self.pageViewController.dataSource = self
self.pageViewController.delegate = self
for a in [0,1,2,3,4]{
var vc: ContentViewController = self.storyboard?.instantiateViewControllerWithIdentifier("ContentViewController") as! ContentViewController
vc.pageIndex = a
vc.tableView.reloadData()
self.viewControllers.append(vc)
}
print(viewControllers)
self.pageViewController.setViewControllers([self.viewControllers[0]] as [UIViewController], direction: .Forward, animated: false, completion: nil)
self.pageViewController.view.frame = CGRectMake(0, 0, self.view.frame.width, self.view.frame.size.height)
self.addChildViewController(self.pageViewController)
self.view.addSubview(self.pageViewController.view)
self.pageViewController.didMoveToParentViewController(self)
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController?
{
index--
if index <= -1 {
index = 4
}
return self.viewControllers[index] as UIViewController
}
func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
index++
if index >= 5 {
index = 0
}
return self.viewControllers[index] as UIViewController
}
func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int
{
return 5
}
func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int
{
return index
}
}
Related
I've got a working UIPageViewController that holds multiple UIViewControllers embedded in an UINavigationController and each UIViewController has a preview of an array of images wich, when opened, instantiate a new UIPageViewController to display those images
when i swipe through the images and then swipe back to the first one my app crashes with "EXC_BAD_ACCESS(code=EXC_I386_GPFLT)" same thing when i use the back button of the UINavigationController
why is that and how can i fix this ?
My PageViewController (the marked line is the last one i got in the debugger before it crashes):
class DetailPageMasterViewController: UIPageViewController, UIPageViewControllerDelegate, UIPageViewControllerDataSource
{
var presentationPageIndex: Int = 0
var itemsArray = [Aktion]()
var pageViewController: UIPageViewController!
#IBOutlet weak var btnEditOutlet: UIBarButtonItem!
#IBAction func btnEditAction(sender: AnyObject)
{
}
override func viewDidLoad()
{
super.viewDidLoad()
self.pageViewController = UIPageViewController.init(transitionStyle: .Scroll,
navigationOrientation: .Horizontal,
options: nil)
self.pageViewController.delegate = self
self.pageViewController.dataSource = self
self.presentationPageIndex = 0
let firstVC = self.viewControllerAtIndex(presentationPageIndex)
let viewControllers = [firstVC]
self.pageViewController.setViewControllers(viewControllers,
direction: .Forward,
animated: false,
completion: nil)
self.addChildViewController(self.pageViewController)
self.view.addSubview(self.pageViewController.view)
self.pageViewController.didMoveToParentViewController(self)
self.setupPageControl()
}
func pageViewController(pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool)
{
if completed
{
let minionVC = self.pageViewController.viewControllers?.last as! DetailMinionViewController
presentationPageIndex = minionVC.pageIndex
}
}
func viewControllerAtIndex(index: Int) -> DetailMinionViewController
{
let contentVC = self.storyboard?.instantiateViewControllerWithIdentifier("MinionPageViewController") as! DetailMinionViewController
contentVC.aktion = itemsArray[index]
contentVC.pageIndex = index
return contentVC
}
func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController?
{
if let viewController = viewController as? DetailMinionViewController
{
var index = viewController.pageIndex
if index == 0 || index == NSNotFound
{
return nil //MARKED LINE
}
index -= 1
return self.viewControllerAtIndex(index)
}
return nil
}
func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController?
{
if let viewController = viewController as? DetailMinionViewController
{
var index = viewController.pageIndex
if index == NSNotFound
{
return nil
}
index += 1
if index == NSNotFound || index >= itemsArray.count
{
return nil
}
return self.viewControllerAtIndex(index)
}
return nil
}
func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int
{
return itemsArray.count
}
func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int
{
return presentationPageIndex
}
func setupPageControl()
{
UIPageControl.appearance().backgroundColor = UIColor.clearColor()
UIPageControl.appearance().pageIndicatorTintColor = UIColor.whiteColor()
UIPageControl.appearance().currentPageIndicatorTintColor = UIColor.redColor()
}
}
So the structure looks like
UINavigationController -> DetailPageMasterViewController -> DetailMinionViewController -> PicturesPageMasterViewController -> PicturesMinionViewController
So i finally figured out what caused my app to crash.
Long story short:
a gestureRecognizer in the PicturesMinionViewController tried to access an already deinitialized ImageView, my pageViewController worked fine
Please check the number of viewController added into the PageController. If possible you can share your code.
//For Navigation Bar Tint and BG Color
var NavBarBGColor = CommonUtilities.RGBColor("#2574A9")
var NavBarTintColor = UIColor.whiteColor()
var pageViewController: UIPageViewController!
var pageTitles: NSArray!
var pageImages: NSArray!
#IBOutlet weak var Open: UIBarButtonItem!
override func viewDidLoad() {
//Navigation Bar Tint and BG Color
self.navigationController?.navigationBar.tintColor = NavBarTintColor
self.navigationController?.navigationBar.barTintColor = NavBarBGColor
super.viewDidLoad()
//Menu Button Click
Open.target = self.revealViewController()
Open.action = Selector("revealToggle:")
//Pan Gesture Recognizer for page navigation
self.view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
//Page View Controller for Men
self.pageTitles = NSArray(objects: "Try","And")
self.pageImages = NSArray(objects: "page1","page2")
self.pageViewController = self.storyboard?.instantiateViewControllerWithIdentifier("PageViewController") as! UIPageViewController
self.pageViewController.dataSource = self.pageViewController.dataSource
/*var startVC = self.viewControllerIndex(0) as ContentMenuViewController
var viewControllers = NSArray(object: startVC)
self.pageViewController.ViewControllers(viewControllers, direction: .Forward, animated: true, completion: nil)*/
self.pageViewController.view.frame = CGRectMake(0, 30, self.view.frame.width, self.view.frame.size.height - 60)
self.addChildViewController(self.pageViewController)
self.view.addSubview(self.pageViewController.view)
self.pageViewController.didMoveToParentViewController(self)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func MenuRestartBtn(sender: AnyObject) {
}
func viewControllerAtIndex(index: Int) -> ContentMenuViewController
{
if ((self.pageTitles.count == 0)) || (index >= self.pageTitles.count)
{
return ContentMenuViewController()
}
var vc: ContentMenuViewController = self.storyboard?.instantiateViewControllerWithIdentifier("") as! ContentMenuViewController
vc.imageFile = self.pageImages[index] as! String
vc.titleText = self.pageTitles[index] as! String
vc.pageIndex = index
return vc
}
//Page View Controller Data Source
func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
var vc = viewController as! ContentMenuViewController
var index = vc.pageIndex as Int
if (index == 0 || index == NSNotFound)
{
return nil
}
index--
return self.viewControllerAtIndex(index)
}
func pageViewController(pageController: UIPageViewController, ViewControllerAfterViewController viewController: UIViewController) -> UIViewController{
var vc = viewController as! ContentMenuViewController
var index = vc.pageIndex as Int
if (index == NSNotFound){
return nil;
}
index ++
if (index == self.pageTitles.count){
return nil
}
return self.viewControllerAtIndex(index)
}
func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int {
return self.pageTitles.count
}
func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int {
return 0
}
}
You cannot return nil when function should return UIViewController wich your trying to do in:
func pageViewController(pageController: UIPageViewController, ViewControllerAfterViewController viewController: UIViewController) -> UIViewController
Replace above with:
func pageViewController(pageController: UIPageViewController, ViewControllerAfterViewController viewController: UIViewController) -> UIViewController?
I'm new to protocols and pageview controllers and I tried to something I hoped was simple. To pass a variable value from the content viewcontroller to the page list viewcontroller but it doesn't work so I'm doing something wrong...
In my storyboard I have a ViewController (as the Page List viewcontroller) and embedded in a navigation controller. The navigation bar has a button on it.
There is a PageContentViewController that has a label.
The code I use follows (EDITED with the accepted solution provided by #Tom Elliott):
protocol setRoomOnScreenProtocol {
func updateRoomOnScreen(myRoom: String)
}
class ViewController: UIViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate, setRoomOnScreenProtocol {
#IBAction func press1(sender: AnyObject) {
println("roomOnScreen: \(roomOnScreen)")
println("pageTitles: \(pageTitles)")
}
#IBOutlet weak var btn1: UIBarButtonItem!
let pageTitles = ["Room 1", "Room 2", "Room 3", "Room 4"]
var count = 0
var pageViewController : UIPageViewController!
var roomOnScreen: String = ""
override func viewDidLoad() {
super.viewDidLoad()
reset()
}
func updateRoomOnScreen(myRoom: String) {
roomOnScreen = myRoom
println("roomOnScreen (PGL): \(roomOnScreen)")
}
func reset() {
/* Getting the page View controller */
pageViewController = self.storyboard?.instantiateViewControllerWithIdentifier("PageViewController") as! UIPageViewController
self.pageViewController.dataSource = self
let pageContentViewController = self.viewControllerAtIndex(0)
self.pageViewController.setViewControllers([pageContentViewController!], direction: UIPageViewControllerNavigationDirection.Forward, animated: true, completion: nil)
self.pageViewController.view.frame = CGRectMake(0, 0, self.view.frame.width, self.view.frame.height)
self.addChildViewController(pageViewController)
self.view.addSubview(pageViewController.view)
self.pageViewController.didMoveToParentViewController(self)
}
func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
var index = (viewController as! PageContentViewController).pageIndex!
index++
return self.viewControllerAtIndex(index)
}
func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
var index = (viewController as! PageContentViewController).pageIndex!
if (index <= 0) {
return nil
}
index--
return self.viewControllerAtIndex(index)
}
func viewControllerAtIndex(index : Int) -> UIViewController? {
if ((self.pageTitles.count == 0) || (index >= self.pageTitles.count)) {
return nil
}
let pageContentViewController = self.storyboard?.instantiateViewControllerWithIdentifier("PageContentViewController") as! PageContentViewController
pageContentViewController.delegate = self
pageContentViewController.titleText = self.pageTitles[index]
pageContentViewController.pageIndex = index
return pageContentViewController
}
func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int {
return pageTitles.count
}
func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int {
return 0
}
}
and the PageContentViewController:
class PageContentViewController: UIViewController {
var pageIndex: Int?
var titleText : String!
var delegate: setRoomOnScreenProtocol!
#IBOutlet weak var label1: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
self.label1.text = self.titleText
}
override func viewDidAppear(animated: Bool) {
if let actualDelegate = self.delegate {
actualDelegate.updateRoomOnScreen(titleText)
} else {
println("error")
}
doMore()
}
func doMore() {
println("showing...:\(titleText)")
}
}
When I move through the pages I correctly get the output:
roomOnScreen (PGL): Room 1
showing...:Room 1
roomOnScreen (PGL): Room 2
showing...:Room 2
But when I hit the navigation bar button I get no value...
roomOnScreen:
pageTitles: [Room 1, Room 2, Room 3, Room 4]
What am I doing wrong...?
Thanks!
The line:
self.delegate = ViewController()
Is actually creating a brand new instance of ViewController each time it is called, so the original ViewController that is visible is not receiving the message.
I'd suggest setting the delegate when you initially create the pages:
let pageContentViewController = self.storyboard?.instantiateViewControllerWithIdentifier("PageContentViewController") as! PageContentViewController
pageContentViewController.delegate = self
I am Using UIPageController having 2 images in it. While running the app, The UIPageController shows the first page perfectly, but then scrolls infinitely from the second page. It maintains the same image and continues to show the similar image even after it has reached the limit. It only changes the image for the first swipe, and then neither changes the image nor revert back to the initial image on the subsequent scroll.
Given is the source. Please help.
import UIKit
class SignUpViewController: UIViewController, UIPageViewControllerDataSource {
var pageViewController : UIPageViewController!
var InfoImages : NSArray!
override func viewDidLoad() {
super.viewDidLoad()
self.InfoImages = NSArray(objects: "blue","red")
self.pageViewController = self.storyboard?.instantiateViewControllerWithIdentifier("IntroPageController") as! UIPageViewController
self.pageViewController.dataSource = self
let startVC = self.viewControllerAtIndex(0) as ContentViewController
let viewControllers = NSArray(object: startVC)
self.pageViewController.setViewControllers(viewControllers as? [UIViewController], direction: .Forward, animated: true, completion: nil)
self.pageViewController.view.frame = CGRectMake(0, 0, self.view.frame.width, self.view.frame.height)
self.addChildViewController(self.pageViewController)
self.view.addSubview(self.pageViewController.view)
self.pageViewController.didMoveToParentViewController(self)
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func viewControllerAtIndex(index: Int) -> ContentViewController
{
if ((self.InfoImages.count == 0) || (index >= self.InfoImages.count)){
return ContentViewController()
}
let info = storyBoard.instantiateViewControllerWithIdentifier("ContentViewController") as! ContentViewController
info.imageFile = self.InfoImages[index] as! String
return info
}
//MARK : -DataSource
func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController?
{
let vc = viewController as! ContentViewController
var index = vc.pageIndex as Int
if( index == 0 || index == NSNotFound ){
return nil
}
index--
return self.viewControllerAtIndex(index)
}
func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController?
{
let vc = viewController as! ContentViewController
var index = vc.pageIndex as Int
if( index == NSNotFound ){
return nil
}
index++
if(index == self.InfoImages.count){
return nil
}
return self.viewControllerAtIndex(index)
}
#available(iOS 6.0, *)
func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int
{
return self.InfoImages.count
}
#available(iOS 6.0, *)
func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int
{
return 0
}
#IBAction func SkipInfo(sender: AnyObject) {
let skipinfo = storyBoard.instantiateViewControllerWithIdentifier("afterLoginview") as! LoginViewController
self.presentViewController(skipinfo , animated: true, completion: nil)
}
Cheers and Thanks in advance!
P.S : I am using Xcode 7.0 beta (swift 2.0)
As you can see, there are two UIViewControllers (one of them is called ProView and acts as a container for the other UIViewController and one UIPageViewController.
import UIKit
class ProView: UIViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate {
var pageViewController: UIPageViewController?
let characterImages = ["character1", "character2"]
override func viewDidLoad() {
super.viewDidLoad()
createPageViewController()
setupPageControl()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func pageViewController(pageViewController: UIPageViewController,
viewControllerAfterViewController ProView: UIViewController) -> UIViewController? {
let itemController = ProView as PageItemController
if itemController.itemIndex+1 < characterImages.count {
return getItemController(itemController.itemIndex+1)
}
return nil
}
func pageViewController(pageViewController: UIPageViewController,
viewControllerBeforeViewController ProView: UIViewController) -> UIViewController? {
let itemController = ProView as PageItemController
if itemController.itemIndex > 0 {
return getItemController(itemController.itemIndex-1)
}
return nil
}
private func getItemController(itemIndex: Int) -> PageItemController? {
if itemIndex < characterImages.count {
let pageItemController = self.storyboard!.instantiateViewControllerWithIdentifier("ItemController") as PageItemController
pageItemController.itemIndex = itemIndex
pageItemController.imageName = characterImages[itemIndex]
return pageItemController
}
return nil
}
func createPageViewController() {
let pageController = self.storyboard!.instantiateViewControllerWithIdentifier("PageController") as UIPageViewController
pageController.dataSource = self
if characterImages.count > 0 {
let firstController = getItemController(0)!
let startingViewControllers: NSArray = [firstController]
pageController.setViewControllers(startingViewControllers, direction: UIPageViewControllerNavigationDirection.Forward, animated: false, completion: nil)
}
pageViewController = pageController
addChildViewController(pageViewController!)
self.view.addSubview(pageViewController!.view)
pageViewController?.didMoveToParentViewController(self)
}
func setupPageControl() {
let appearance = UIPageControl.appearance()
appearance.pageIndicatorTintColor = UIColor.grayColor()
appearance.currentPageIndicatorTintColor = UIColor.whiteColor()
}
func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int {
return characterImages.count
}
func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int {
return 0
}
}
class PageItemController: UIViewController {
#IBOutlet weak var imageCharacterChoose: UIImageView!
var itemIndex: Int = 0
var imageName: String = "" {
didSet {
if let imageView = imageCharacterChoose {imageCharacterChoose.image = UIImage(named: imageName)
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
imageCharacterChoose!.image = UIImage(named: imageName)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
I would like to know if the user correctly swipes from one view to another. For that matter, I need the method transitionCompleted to work:
func pageViewController(ProView: UIPageViewController,
didFinishAnimating finished: Bool,
previousViewControllers pageViewController: [AnyObject],
transitionCompleted completed: Bool)
{
// If the page did not turn
if (!completed)
{
// You do nothing because whatever page you thought
// the book was on before the gesture started is still the correct page
println("The page number did not change.")
return;
}
// This is where you would know the page number changed and handle it appropriately
println("The page number has changed.")
}
The problem is that doesn't work (the console output doesn't print anything) because the parameters aren't correct probably. According to Apple Reference,
pageViewController The page view controller.
previousViewControllers The view controllers prior to the transition.
I therefore changed the parameters using "ProView" and "pageViewController" but that doesn't seem to work. What are the correct parameters according to the code for the transitionCompleted method? Do I need to declare something?
The problem is that pageViewController:didFinishAnimating:... is a delegate method, but this class is not the page view controller's delegate.
You have this line:
pageController.dataSource = self
So you are setting the dataSource. But you never set the delegate. Set it:
pageController.delegate = self