Delete Image from UIPageViewController - swift

I have a PageViewController set up so that I can add photos from camera or library and the pageviewcontroller resets itself every time the imagepickercontroller is called so the pageviewcontrol updates and shows. It works perfectly (i think). What i know want to do is have the option to delete one of the photos then call the same reset.
The problem is, i can not get an accurate index value of the current image being displayed in the pageviewcontroller. I try to grab the index from the viewControllerBeforeViewController or AfterViewController or the pagecontrolleratindex... but the index does not follow correctly the way the pagecontrol is designed. I read about willtransitiontoviewcontrollers and didfinsihanimating, but those are not being called, i am thinking because i am getting the images from an array and not new view controllers.
My method to delete works, but the index is not accurate so even though the second image is shown the first image could be the one deleted.
Is there a good way to get the current displayed array index so i can delete the image from array? //this array holds images from imagepicker, i call the restartviewcontrollers evertime there is an imagepicked to update the number of pages and this works.
UPDATE
var pageViewController:UIPageViewController!
var pageImages: [UIImage]? = [] //this array holds images from imagepicker, i call the restartviewcontrollers evertime there is an imagepicked to update the number of pages and this works.
var deletePageIndex: Int = 0
func pageViewControllerAtIndex(index: Int) -> PhotoContentPageViewController {
if (self.pageImages!.count == 0 || index >= self.pageImages!.count) {
return PhotoContentPageViewController()
}
var vc = self.storyboard?.instantiateViewControllerWithIdentifier("PhotoPageContentStoryBoard") as! PhotoContentPageViewController
vc.imageFileName = pageImages![index] as! UIImage
vc.pageIndex = index
return vc
}
func restartViewController() {
var startVc = self.pageViewControllerAtIndex(0) as PhotoContentPageViewController
var viewControllers = NSArray(object: startVc)
self.pageViewController.setViewControllers(viewControllers as? [UIViewController], direction: .Forward, animated: true, completion: nil)
}
func startViewControllers() {
self.pageViewController = self.storyboard?.instantiateViewControllerWithIdentifier("PhotoPageViewControllerStoryBoard") as! UIPageViewController
self.pageViewController.dataSource = self
pageViewController.delegate = self
var startVc = self.pageViewControllerAtIndex(0) as PhotoContentPageViewController
var viewControllers = NSArray(object: startVc)
self.pageViewController.setViewControllers(viewControllers as? [UIViewController], direction: .Forward, animated: true, completion: nil)
self.pageViewController.view.frame = CGRectMake(0, -10, self.view.frame.size.width, self.view.frame.size.height-30)
self.addChildViewController(self.pageViewController)
self.view.addSubview(self.pageViewController.view)
self.pageViewController.didMoveToParentViewController(self)
}
func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
var vc = viewController as! PhotoContentPageViewController
var index = vc.pageIndex as Int
if (index == 0 || index == NSNotFound) {
return nil
}
index--
return self.pageViewControllerAtIndex(index)
}
func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
var vc = viewController as! PhotoContentPageViewController
var index = vc.pageIndex as Int
if (index == NSNotFound) {
return nil
}
index++
if (index == self.pageImages!.count) {
return nil
}
return self.pageViewControllerAtIndex(index)
}
func pageViewController(pageViewController: UIPageViewController, willTransitionToViewControllers pendingViewControllers: [UIViewController]) {
}
func pageViewController(pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
if (!completed) {
return
}
}
`

If you actually are using a UIPageViewControllerDelegate, you can use willtransitiontoviewcontrollers but that method only works if you set the delegate property of the page view controller to your view controller. That way, you can simply get the index of the current view controller in page view controller and use it for deletion.
myPageViewController.delegate = self
And make sure your view controller implements UIPageViewControllerDelegate
UPDATE
Assuming that you have an array of you view controllers, you could implement your data source like this:
func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
if let index = pages.indexOf(viewController)?.advancedBy(1) where index < pages.count {
return pages[index]
}
return nil
}
func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
if let index = pages.indexOf(viewController)?.advancedBy(-1) where index > -1 {
return pages[index]
}
return nil
}

Related

Issue Implementing UIPageViewController

I am trying to implement to scroll between two view controllers, one to be a message page, and the other view controller to be a camera page, similar to snapchat app. If I didn't make myself understood I attached an image with the app that has exactly the same implementation as I'm trying to do, it has 5 views which you can scroll between them. image
Now I have tried it to implement in multiple ways. One of them is with UIPageViewController, snippet of my code:
class ViewController: UIViewController , UIPageViewControllerDelegate
{
var chatVC = one()
var cameraVC= two()
var array = [UIViewController]()
var arr = [UIViewController]()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
self.array = [chatVC,cameraVC]
self.arr = [chatVC]
DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: {
self.presentPageVC()
})
}
func presentPageVC(){
var vc = UIPageViewController.init(transitionStyle: .scroll, navigationOrientation: .horizontal , options: nil)
vc.delegate = self
vc.dataSource = self
vc.setViewControllers(self.arr, direction: .forward, animated: true, completion: nil)
self.present(vc, animated: true, completion: nil)
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let index = self.array.firstIndex(of: viewController),index>0 else {return nil}
let beforeIndex = index - 1
return self.array[beforeIndex]
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let index = self.array.firstIndex(of: viewController), index < (self.array.count-1) else {
return nil
}
var indexAfter = index + 1
return self.array[indexAfter]
}
With this implementation I have one problem as I try to scroll after the last page on the cameraVC, on the right it appears the view behind it. How could I turnOff scrolling when there is no other page after or before.
As well, Im new to software development, if somebody has a better idea for implementing it, please let me know. I get stuck at trying to implementing with UICollectionView because I don't know how to convert a UIViewController to a cell, and how to change depending on it on the delegate. As well when trying to add via UIScrollView, I don't understand how I could a UIViewController subview to the UIScrollview, because it accepts only UIView.
Thank you in advance for the help,
For anybody who wants the UIScrollView to don't show the viewUnder when scrolling there are no existent views to scroll.
scrollView.showsLargeContentViewer = false

UIPageViewController show the wrong page after adding third page

in my project I have a UIPageViewController that user can add new pages to it(it start with one page). every time user add new page UIPageViewController must show the current page(the user must be in the last page so he/she can add new page). after adding second page every things going fine but when user is in page2 and adding third page and scroll forward, UIPageViewController show the first page instead of showing the third page(for some reason I don't know, if user scroll backward everything working fine and UIPageViewController showing page1).
this is my codes to add new page:
class HomePageVC: UIPageViewController {
private var pages = [SomeVC]()
private var currentSomeVC: SomeVC!
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .clear
dataSource = self
delegate = self
let someVC = SomeVC()
pages.append(someVC)
if pages.count != 0 {
currentSomeVC = pages[0]
setViewControllers(pages, direction: .forward, animated: true, completion: nil)
}
if pages.count == 1 {
delegate = nil
dataSource = nil
}
}
func addNewPage() {
let someVC = SomeVC()
pages.append(someVC)
if delegate == nil, dataSource == nil {
delegate = self
dataSource = self
}
}
}
extension HomePageVC: UIPageViewControllerDelegate, UIPageViewControllerDataSource {
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = pages.firstIndex(of: viewController as! SomeVC) else {
return nil
}
let previousIndex = viewControllerIndex - 1
guard previousIndex >= 0 else {
return pages.last
}
guard pages.count > previousIndex else {
return nil
}
return pages[previousIndex]
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = pages.firstIndex(of: viewController as! SomeVC) else {
return nil
}
let nextIndex = viewControllerIndex + 1
guard pages.count != nextIndex else {
return pages.first
}
guard pages.count > nextIndex else {
return nil
}
return pages[nextIndex]
}
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
if completed {
guard let viewControllerIndex = pages.firstIndex(of: pageViewController.viewControllers?.first as! SomeVC), let viewController = pageViewController.viewControllers?.first as? SomeVC else {
return
}
currentSomeVC = viewController
}
}
}
Try replacing your addNewPage() method with the following:
func addNewPage() {
let someVC = SomeVC()
pages.append(someVC)
if delegate == nil, dataSource == nil {
delegate = self
dataSource = self
}
setViewControllers([pages[pages.count-1]], direction: .forward, animated: true, completion: nil)
}
You're adding new viewControllers to your pageViewController but not setting you pageViewController to newly created one.
Hope It'll work for you.
Reload UIPageViewController,
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
if !completed { return }
DispatchQueue.main.async() {
self.dataSource = nil
self.dataSource = self
}
}

UIPageViewController load ChildViews multiple times

I've subclassed UIPageViewController, and it works fine. Except the fact that going back and forth load new instances of the pages, instead of (as i would expect) retrieving the instance it loaded previously.
Aren't the references supposed to be strong?
Following is my code.
Thank you for your help.
Pierrick
class CustomPageViewController: UIPageViewController, UIPageViewControllerDataSource {
var view1: FirstCustomViewController!
var view2: SecondCustomViewController!
var view3: ThirdCustomViewController!
var views = []
override func viewDidLoad() {
super.viewDidLoad()
self.dataSource = self;
self.view1 = FirstViewController()
self.view2 = SecondViewController()
self.view3 = ThirdViewController()
self.views = [self.view1, self.view2, self.view3]
self.setViewControllers([self.view1], direction: UIPageViewControllerNavigationDirection.Forward, animated: true, completion: nil)
}
// MARK: - PageViewControllerDataSource
func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
let index = self.views.indexOfObject(viewController)
if (index > 0) {
return self.views.objectAtIndex(index-1) as? UIViewController
}
return nil
}
func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
let index = self.views.indexOfObject(viewController)
if (index < self.views.count-1) {
return self.views.objectAtIndex(index+1) as? UIViewController
}
return nil
}
}
Seems like the faulty function is in FirstViewController, SecondViewController, ThirdViewController. When i wrote my question, the view controller loaded the view following the method "viewWillAppear".
As soon as i moved the code to "viewDidLoad", the views stopped to load multiple times.

UIPageViewController : move programmatically page doesn't work

I try to turn the page by touching a button on one of the two navigation controllers(each navigationcontroller is related to a viewcontroller) but when i call a function in the pageviewcontroller to change index and change page it doesn't work at all...
I tried setViewController.
I would like to do like in the snapchat app, a navbuttonitem which can slide to the left or to the right.
Thank you in advance
here is the code of the pageviewcontroller :
class PageViewController: UIPageViewController,UIPageViewControllerDataSource, UIPageViewControllerDelegate{
var index = 0
var identifiers: NSArray = ["FirstNavigationController", "SecondNavigationController"]
override func viewDidLoad() {
self.dataSource = self
self.delegate = self
let startingViewController = self.viewControllerAtIndex(self.index)
let viewControllers: NSArray = [startingViewController]
self.setViewControllers(viewControllers as [AnyObject], direction: UIPageViewControllerNavigationDirection.Forward, animated: false, completion: nil)
}
func viewControllerAtIndex(index: Int) -> UINavigationController! {
let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
//first view controller = firstViewControllers navigation controller
if index == 0 {
return storyBoard.instantiateViewControllerWithIdentifier("FirstNavigationController") as UINavigationController
}
//second view controller = secondViewController's navigation controller
if index == 1 {
return storyBoard.instantiateViewControllerWithIdentifier("SecondNavigationController") as UINavigationController
}
return nil
}
func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
let identifier = viewController.restorationIdentifier
let index = self.identifiers.indexOfObject(identifier!)
//if the index is the end of the array, return nil since we dont want a view controller after the last one
if index == identifiers.count - 1 {
return nil
}
//increment the index to get the viewController after the current index
self.index = self.index + 1
return self.viewControllerAtIndex(self.index)
}
func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
let identifier = viewController.restorationIdentifier
let index = self.identifiers.indexOfObject(identifier!)
//if the index is 0, return nil since we dont want a view controller before the first one
if index == 0 {
return nil
}
//decrement the index to get the viewController before the current one
self.index = self.index - 1
return self.viewControllerAtIndex(self.index)
}
}
First, why don't you stop each instance of your controllers rather than re-instantiating it ? Here is the line :
func viewControllerAtIndex(index: Int) -> UINavigationController! {
let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
//first view controller = firstViewControllers navigation controller
if index == 0 {
return storyBoard.instantiateViewControllerWithIdentifier("FirstNavigationController") as UINavigationController
}
//second view controller = secondViewController's navigation controller
if index == 1 {
return storyBoard.instantiateViewControllerWithIdentifier("SecondNavigationController") as UINavigationController
}
return nil}
You could create a local array containing all your instances..
Then, to move programmatically between your pages, you can use setViewController this way :
func showAViewController(){
let startVC : NSArray = [viewController]
setViewControllers(startVC as! [AnyObject], direction: .Reverse, animated: true, completion: nil)
}
Where direction is .Reverse or .Forward depending on the position of the destination view controller relative to the present view controller.
Here is an example of a test program I wrote, it worked for me :
class RootVC: UIPageViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate {
var viewController1 : UIViewController!
var viewController2 : UIViewController!
override func viewDidLoad() {
super.viewDidLoad()
dataSource = self
viewController1 = storyboard?.instantiateViewControllerWithIdentifier("viewController1") as! UIViewController
viewController1.title = "viewController1"
viewController2 = storyboard?.instantiateViewControllerWithIdentifier("viewController2") as! UIViewController
viewController2.title = "viewController2"
var startingVC : NSArray = [viewController1]
setViewControllers(startingVC as! [AnyObject], direction: .Forward, animated: false, completion: nil)
Notifications.addObserver(self, selector: "showViewController1", name: "showViewController1", object: nil)
Notifications.addObserver(self, selector: "showViewController2", name: "showViewController2", object: nil)
}
deinit{
Notifications.removeObserver(self)
}
func showViewController1(){
let startVC : NSArray = [viewController1]
setViewControllers(startVC as! [AnyObject], direction: .Forward, animated: true, completion: nil)
}
func showViewController2(){
let startVC : NSArray = [viewController1]
setViewControllers(startVC as! [AnyObject], direction: .Forward, animated: true, completion: nil)
}
func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
switch viewController.title!{
case "viewController1": return nil
case "viewController2": return viewController1
default: return nil
}
}
func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
switch viewController.title!{
case "viewController1": return viewController2
case "viewController2": return viewController1
default: return nil
}
}}
NSNotificationsis not a mandatory, but I like to use it to avoid heavy delegation.

Swift - Bringing UIPageViewController controls to front

I'm having some trouble bringing the dotted UIPageControls to the front after I filled out the whole screen.
Here's my current code:
import UIKit
class ViewController: UIViewController, UIPageViewControllerDataSource {
//This outlet wasn't originally there. Added a UIPageControl as an attempt to solve my issue
#IBOutlet var pageControl: UIPageControl!
var pageViewController : UIPageViewController?
var pageTitles : Array<String> = ["Orc", "Elven", "Undead"]
var pageImages : Array<String> = ["Plain_Orc.png", "Plain_Elf.png", "Plain_Undead.png"]
var currentIndex : Int = 0
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.pageViewController = UIPageViewController(transitionStyle: .Scroll, navigationOrientation: .Horizontal, options: nil)
self.pageViewController!.dataSource = self
let startingViewController: PageContentViewController = self.viewControllerAtIndex(0)!
let viewControllers: NSArray = [startingViewController]
self.pageViewController!.setViewControllers(viewControllers, direction: .Forward, animated: false, completion: nil)
self.pageViewController!.view.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height + 37);
self.addChildViewController(self.pageViewController!)
//self.view.addSubview(self.pageViewController!.view)
//replacing addSubview from insane-36
self.view.insertSubview(pageViewController!.view, belowSubview: self.pageControl!)
self.pageViewController!.didMoveToParentViewController(self)
//Bring pagecontrols to front? (DOsESN'T WORK)
//self.view.bringSubviewToFront(self.pageViewController!.view)
//attempt to fix above code from insane-36
view.bringSubviewToFront(self.pageControl!)
view.sendSubviewToBack(self.pageViewController!.view)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController?
{
var index = (viewController as PageContentViewController).pageIndex
if (index == 0) || (index == NSNotFound) {
return nil
}
index--
println("Decreasing Index: \(String(index))")
return self.viewControllerAtIndex(index)
}
func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController?
{
var index = (viewController as PageContentViewController).pageIndex
if index == NSNotFound {
return nil
}
index++
println("Increasing Index: \(String(index))")
if (index == self.pageTitles.count) {
return nil
}
return self.viewControllerAtIndex(index)
}
func viewControllerAtIndex(index: Int) -> PageContentViewController?
{
if self.pageTitles.count == 0 || index >= self.pageTitles.count
{
return nil
}
// Create a new view controller and pass suitable data.
let pageContentViewController = self.storyboard?.instantiateViewControllerWithIdentifier("PageContentViewController") as PageContentViewController
pageContentViewController.imageFile = self.pageImages[index]
pageContentViewController.titleText = self.pageTitles[index]
pageContentViewController.pageIndex = index
self.currentIndex = index
return pageContentViewController
}
func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int
{
return self.pageTitles.count
}
func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int
{
return 0
}
}
I thought the line that brings the pageViewController view to front would fix my issue, but it doesn't work. Hopefully someone can give me a push in the right direction :)
Use any one of the following method to send the UIPageViewController's view to the back of the UIPageControl,
Instead of using addSubview: to add UIPageViewController's
view, use
view.insertSubview(pageViewController.view, belowSubview:self.pageControl)
Use bringSubviewToFront: method,
view.bringSubviewToFront(self.pageControl) // after you have added the UIPageViewController's view
Use sendSubviewToBack: method,
view.sendSubviewToBack(self.pageViewController.view)