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
Related
I have an UIViewController with a UITableView. Then I create a PageViewController with two viewcontroller. Pageview doesn't work if I pass data to child. If I change
setViewControllers([subjective], direction: .Forward, animated: true, completion: nil)
to
setViewControllers([firstVC], direction: .Forward, animated: true, completion: nil)
Pageview work but data don't pass. I don't have any idea about it. Help me please. Thanks
Code Work:
pageview controller class:
import UIKit
class PageVC : UIPageViewController, UIPageViewControllerDelegate, UIPageViewControllerDataSource {
var lblhoten = String()
var lblngaysinh = String()
var lblsodt = String()
lazy var VCArr: [UIViewController] = {
return [self.VCInstance("ThongTinBNPage2"),
self.VCInstance("ThongTinBNPage3")]
}()
private func VCInstance(name: String) -> UIViewController {
return UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier(name)
}
override func viewDidLoad() {
super.viewDidLoad()
self.dataSource = self
self.delegate = self
if let firstVC = VCArr.first {
let subjective = self.storyboard?.instantiateViewControllerWithIdentifier("ThongTinBNPage2") as! VCSubjective
subjective.lblhoten = lblhoten
subjective.lblngaysinh = lblngaysinh
subjective.lblsodt = lblsodt
setViewControllers([subjective], direction: .Forward, animated: true, completion: nil)
}
}
public func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
guard let viewcontrollerindex = VCArr.indexOf(viewController) else {
return nil
}
let previousindex = viewcontrollerindex - 1
guard previousindex >= 0 else {
return VCArr.last
}
guard VCArr.count > previousindex else {
return nil
}
return VCArr[previousindex]
}
public func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
guard let viewcontrollerindex = VCArr.indexOf(viewController) else {
return nil
}
let nextindex = viewcontrollerindex + 1
guard nextindex < VCArr.count else {
return VCArr.first
}
guard VCArr.count > nextindex else {
return nil
}
return VCArr[nextindex]
}
public func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int {
return VCArr.count
}
// The selected item reflected in the page indicator.
public func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int {
guard let firstviewcontroller = viewControllers?.first,
let firstviewcontrollerindex = VCArr.indexOf(firstviewcontroller) else {
return 0
}
return firstviewcontrollerindex
}
}
VCSubjective:
import UIKit
class VCSubjective: UIViewController {
#IBOutlet var hoten: UILabel!
#IBOutlet var ngaysinh: UILabel!
#IBOutlet var sodt: UILabel!
var lblhoten = String()
var lblngaysinh = String()
var lblsodt = String()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
hoten.text = lblhoten
ngaysinh.text = lblngaysinh
sodt.text = lblsodt
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
The problem here is that you are referencing two completely different objects.
In viewDidLoad you create ThongTinBNPage2 viewController and then add it to the viewControllers property of the pageViewController. However, the objects stored in VCArr are two totally different viewControllers.
Let's think about it this way:
When viewDidLoad is called you create viewController object #1
Then you assign the viewController object #1 to the viewControllers object of the pageViewController making the value of viewControllers = [object #1]
In pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? and pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? you reference the VCArr object. On the first call to VCArr, it lazily creates two completely different view controller objects [object #2, object #3]
To fix this code you need to do the following:
if let firstVC = VCArr.first {
let subjective = firstVC // DO NOT create different VC here
subjective.lblhoten = lblhoten
subjective.lblngaysinh = lblngaysinh
subjective.lblsodt = lblsodt
setViewControllers([subjective], direction: .Forward, animated: true, completion: nil)
}
I have a UIPageViewController in my program. I have added a UITextView programmatically at the top of UIPageViewController.I want to update the text of UITextView with each swipe to page 1 of n or 2 of n or ….
How should I update the TextView to have the correct image number? Where should I call updateStatusLabelText function?
The reason why I used UITextView is that I could not add a UILabel with constraints through code to the top of UIPageViewController.
First class is as follows:
class MainPageViewController: UIPageViewController {
let dS = MainPageViewControllerDataSource()
var statusText : String = ""
var statusTextView = UITextView()
override func viewDidLoad() {
super.viewDidLoad()
statusTextView = UITextView(frame: view.bounds)
statusTextView.text = statusText
statusTextView.editable = false
statusTextView.textAlignment = .Center
statusTextView.font = UIFont(name: statusTextView.font!.fontName, size: 18)
statusTextView.textColor = UIColor.blackColor()
view.backgroundColor = UIColor.whiteColor()
view.addSubview(statusTextView)
view.sendSubviewToBack(statusTextView)
}
func updateStatusLabelText(arrayKey:Int){
statusTextView.text = "\(arrayKey + 1) of \(imageArray.count) "
}
override func viewWillAppear(animated: Bool) {
dataSource = dS
setViewControllers([dS.viewControllerAtIndex(0) as TutorialViewController], direction: .Forward, animated: true, completion: nil)
}
}
Second class
class MainPageViewControllerDataSource: NSObject, UIPageViewControllerDataSource,UIPageViewControllerDelegate {
var tutorialViewControllerDelegate: TutorialViewControllerDelegate?
var pageIndex = Int()
func viewControllerAtIndex(index: Int) -> TutorialViewController {
let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("tutorialVC") as! TutorialViewController
vc.myImage = imageArray[index]
vc.pageIndex = index
self.pageIndex = index
return vc
}
func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
var index = (viewController as! TutorialViewController).pageIndex
index -= 1
switch index {
case Int.min ... (-1):
return nil
case NSNotFound:
fatalError("NSNotFound. Should crash.")
case imageArray.count..<Int.max:
return nil
default:
return viewControllerAtIndex(index)
}
}
func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
var index = (viewController as! TutorialViewController).pageIndex
index += 1
switch index {
case Int.min ... (-1):
return nil
case NSNotFound:
fatalError("NSNotFound. Should crash.")
case imageArray.count ..< Int.max:
return nil
default:
return viewControllerAtIndex(index)
}
}
func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int {
return imageArray.count
}
func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int {
return pageIndex - 1
}
}
Third class:
class TutorialViewController: UIViewController, UIPageViewControllerDelegate {
#IBOutlet weak var imageToPresent: UIImageView!
var itemIndex: Int = 0
var myImage : MyImage?
var pageIndex = Int()
override func viewDidLoad() {
super.viewDidLoad()
imageToPresent = myImage
}
}
//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 was trying to make a tutorial screen in my iOS app where the user swipes past a few pictures to learn a bit about the said application. Everything looks fine, except when I build my project, I get the following error on the very first line of code:
Class ViewController has no initializers
What did I do wrong?
My ViewController.swift code is below
import UIKit
//line below is where I get the error
class ViewController: UIViewController, UIPageViewControllerDataSource {
//end
var pageViewController: UIPageViewController
var pageTitles: NSArray!
var pageImages: NSArray!
override func viewDidLoad() {
super.viewDidLoad()
self.pageTitles = NSArray(objects: "", "", "")
self.pageImages = NSArray(objects: "page1", "page2", "page3")
self.pageViewController = self.storyboard?.instantiateViewControllerWithIdentifier("PageViewController") as!
UIPageViewController
self.pageViewController.dataSource = self
var startVC = self.viewControllerAtIndex(0) as ContentViewController
var viewControllers = NSArray(object: startVC)
self.pageViewController.setViewControllers(viewControllers as [AnyObject], direction: .Forward, animated: true, completion: nil)
self.addChildViewController(self.pageViewController)
self.view.addSubview(self.pageViewController.view)
self.pageViewController.didMoveToParentViewController(self)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func restartAction(sender: AnyObject) {
}
func viewControllerAtIndex(index: Int) -> ContentViewController
{
if ((self.pageTitles.count == 0) || (index >= self.pageTitles.count))
{
return ContentViewController()
}
var vc: ContentViewController = self.storyboard?.instantiateViewControllerWithIdentifier("ContentViewController") as! ContentViewController
vc.imageFile = self.pageImages[index] as! String
vc.titleText = self.pageTitles[index] as! String
vc.pageIndex = index
return vc
}
// Mark: - Page View Controller Data Source
func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
var 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? {
var vc = viewController as! ContentViewController
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
}
}
It's because of this line:
var pageViewController: UIPageViewController
You're saying that pageViewController cannot be null, but you don't have an initializer that sets it to a value. Looking at your code, I suspect you want to change it to this:
var pageViewController: UIPageViewController!
I went into more detail on this problem here.
In a Swift class each non-optional declared variable must be initialized in one of the given init methods.
In your case, it's this variable
var pageViewController: UIPageViewController
declare it as implicit unwrapped optional as the other two variables, because the variable is guaranteed to be set before it's used.
var pageViewController: UIPageViewController!
I am trying to compile my Xcode application. It builds successfully however it doesn't get past the launch screen.
Also It keeps referring me to this particular line of code: instantiateViewControllerWithIdentifier (full code below) and in the logs I get this message:
Unknown class ContentViewController in Interface Builder file.
Could not cast value of type 'UIViewController' (0x10741f418) to 'HelloHi.ContentViewController' (0x1049aa2e0).
(lldb)
Can anyone please help me fix this?
import UIKit
class ViewController: UIViewController, UIPageViewControllerDataSource {
var pageViewController: UIPageViewController!
var pageTitles: NSArray!
var pageImages: NSArray!
override func viewDidLoad() {
super.viewDidLoad()
self.pageTitles = NSArray(objects: "Welcome", "Hello")
self.pageImages = NSArray(objects: "page1", "page2")
self.pageViewController = self.storyboard?.instantiateViewControllerWithIdentifier("PageViewController") as! UIPageViewController
self.pageViewController.dataSource = self
var startVC = self.viewControllerAtIndex(0) as ContentViewController
var viewControllers = [startVC]
self.pageViewController.setViewControllers(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()
// Dispose of any resources that can be recreated.
}
#IBAction func restartAction(sender: AnyObject) {
var startVC = self.viewControllerAtIndex(0) as ContentViewController
var viewControllers = [startVC]
self.pageViewController.setViewControllers(viewControllers, direction: .Forward, animated: true, completion: nil)
}
func viewControllerAtIndex(index: Int) -> ContentViewController
{
if ((self.pageTitles.count == 0) || (index >= self.pageTitles.count))
{
return ContentViewController()
}
var vc: ContentViewController = self.storyboard?.instantiateViewControllerWithIdentifier("ContentViewController") as! ContentViewController
vc.imageFile = self.pageImages[index] as! String
vc.titleText = self.pageTitles[index] as! String
vc.pageIndex = index
return vc
}
//MARK: - Page View Controller Data Source
func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
var 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? {
var vc = viewController as! ContentViewController
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
}
}
The error message indicates that the view controller in the Interface Builder file has the wrong class set. Select the offending controller, open the identity inspector and change the class name to the correct UIViewController subclass. Presumably this is ContentViewController for you.
Of course, you need to define the the ContentViewController class somewhere in the project. It is not in the code you posted, and following the error message, it is not anywhere else in your project.