I am having an issue with my two child view controllers inside a parent PageViewController, where a delegate called by one of the children is not received by the other child.
My first child contains buttons, and when a button is pressed, a delegate is triggered in the other child to pause the timer. However, it fails to receive the call and the timer continues to run.
Here is my PageViewController:
class StartMaplessWorkoutPageViewController: UIPageViewController, UIPageViewControllerDelegate, UIPageViewControllerDataSource {
lazy var workoutViewControllers: [UIViewController] = {
return [self.getNewViewController(viewController: "ButtonsViewController"), self.getNewViewController(viewController: "DisplayMaplessViewController")]
}()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.dataSource = self
// Saw this from another answer, doesn't do anything that helps (at the moment)
let buttonsViewController = storyboard?.instantiateViewController(withIdentifier: "ButtonsViewController") as! ButtonsViewController
let displayMaplessViewController = storyboard?.instantiateViewController(withIdentifier: "DisplayMaplessViewController") as! DisplayMaplessViewController
buttonsViewController.buttonsDelegate = displayMaplessViewController
if let firstViewController = workoutViewControllers.last {
setViewControllers([firstViewController], direction: .forward, animated: true, completion: nil)
}
let pageControl = UIPageControl.appearance(whenContainedInInstancesOf: [StartWorkoutPageViewController.self])
pageControl.currentPageIndicatorTintColor = .orange
pageControl.pageIndicatorTintColor = .gray
}
func getNewViewController(viewController: String) -> UIViewController {
return (storyboard?.instantiateViewController(withIdentifier: viewController))!
}
// MARK: PageView DataSource
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = workoutViewControllers.firstIndex(of: viewController) else {
return nil
}
let previousIndex = viewControllerIndex - 1
guard previousIndex >= 0 else {
return workoutViewControllers.last
}
guard workoutViewControllers.count > previousIndex else {
return nil
}
return workoutViewControllers[previousIndex]
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = workoutViewControllers.firstIndex(of: viewController) else {
return nil
}
let nextIndex = viewControllerIndex + 1
let workoutViewControllersCount = workoutViewControllers.count
guard workoutViewControllersCount != nextIndex else {
return workoutViewControllers.first
}
guard workoutViewControllersCount > nextIndex else {
return nil
}
return workoutViewControllers[nextIndex]
}
func presentationCount(for pageViewController: UIPageViewController) -> Int {
return workoutViewControllers.count
}
func presentationIndex(for pageViewController: UIPageViewController) -> Int {
guard let firstViewController = viewControllers?.first, let firstViewControllerIndex = workoutViewControllers.firstIndex(of: firstViewController) else {
return 0
}
return firstViewControllerIndex
}
}
My ChildViewController with Buttons:
protocol ButtonsViewDelegate: class {
func onButtonPressed(button: String)
}
class ButtonsViewController: UIViewController {
weak var buttonsDelegate: ButtonsViewDelegate?
var isPaused: Bool = false
#IBOutlet weak var startStopButton: UIButton!
#IBOutlet weak var optionsButton: UIButton!
#IBOutlet weak var endButton: UIButton!
#IBAction func startStopButton(_ sender: Any) {
if isPaused == true {
buttonsDelegate?.onButtonPressed(button: "Start")
isPaused = false
} else {
buttonsDelegate?.onButtonPressed(button: "Pause")
isPaused = true
}
}
#IBAction func endButton(_ sender: Any) {
let menu = UIAlertController(title: "End", message: "Are you sure you want to end?", preferredStyle: .actionSheet)
let end = UIAlertAction(title: "End", style: .default, handler: { handler in
self.buttonsDelegate?.onButtonPressed(button: "End")
})
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel)
menu.addAction(end)
menu.addAction(cancelAction)
self.present(menu, animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
My other ChildViewController, which should be receiving the calls of the ButtonsViewDelegate:
import UIKit
class DisplayMaplessViewController: UIViewController, ButtonsViewDelegate {
var timer = Timer()
var currentTime: TimeInterval = 0.0
var isCountdown: Bool = false
var isInterval: Bool = false
var currentRepeats: Int = 0
var currentActivity: Int = 0
var count: Int = 0
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
startIntervalTimer(withTime: 0)
}
// Currently not being called
func onButtonPressed(button: String) {
switch button {
case "Start":
restartIntervalTimer()
case "Pause":
pauseIntervalTimer()
case "End":
stop()
default:
break
}
}
func startIntervalTimer(withTime: Double) {
if withTime != 0 {
currentTime = withTime
if isInterval != true {
isCountdown = true
}
}
timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(intervalTimerUpdate), userInfo: nil, repeats: true)
}
func pauseIntervalTimer() {
timer.invalidate()
}
func restartIntervalTimer() {
timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(intervalTimerUpdate), userInfo: nil, repeats: true)
}
// Currently Not being called
func stop() {
timer.invalidate()
let formatter = DateComponentsFormatter()
formatter.unitsStyle = .positional
formatter.allowedUnits = [.hour, .minute, .second]
formatter.zeroFormattingBehavior = [.pad]
let timeString = formatter.string(from: currentTime)
// save the data etc
print("Stop is called")
}
#objc func intervalTimerUpdate() {
currentTime += 1.0
print(currentTime)
}
}
Sorry that this is so long winded, been trying for quite a while and really annoyed that it doesn't work! Thanks!
I'll try to be clear, hopefully i'll be so as english is not my native language.
It seems to me that you are instantiating your ViewControllers to be presented in the getNewViewController() method and storing them in the workoutViewControllers array, but you are setting the delegate as a separate instance that you never set in your PageVC. You need to set the delegates using the same instances.
These two are two instances of two VC classes (also not sure if the identifier "DisplayViewController" is right, i expected "DisplayMaplessViewController", hard to tell without the storyboard):
let buttonsViewController = storyboard?.instantiateViewController(withIdentifier: "ButtonsViewController") as! ButtonsViewController
let displayMaplessViewController = storyboard?.instantiateViewController(withIdentifier: "DisplayViewController") as! DisplayMaplessViewController
buttonsViewController.buttonsDelegate = displayMaplessViewController
And these in the array two other instances, unrelated from the ones above, of the same two classes:
lazy var workoutViewControllers: [UIViewController] = {
return [self.getNewViewController(viewController: "ButtonsViewController"), self.getNewViewController(viewController: "DisplayMaplessViewController")]
}()
To better understand what i mean, i refactored from scratch and semplified your project (had to do it programmatically as i'm not used to storyboards).
It now consists of a PageController that displays a buttonsVC with a red button and a displayMaplessVC with a blue background.
Once you press the red button, the delegate method is called which causes the blue background to turn green.
Take a look at what i'm doing, as i'm appending the same instances of which i set the delegate:
instantiate a DisplayMaplessViewController object and ButtonsViewController object;
set buttonsVC.buttonsDelegate = displayMaplessVC;
append both ViewControllers to the array.
This is a way to get it done but for sure there are several other ways to achieve the same result, once you get the point and understand your mistake you can pick the one you like the most.
Just copy and paste it into a new project, build and run (you have to set the class of the starting ViewController in the Storyboard as StartMaplessWorkoutPageViewController):
import UIKit
class StartMaplessWorkoutPageViewController: UIViewController, UIPageViewControllerDelegate, UIPageViewControllerDataSource {
private var workoutViewControllers = [UIViewController]()
private let pageController: UIPageViewController = {
let pageController = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil)
return pageController
}()
override func viewDidLoad() {
super.viewDidLoad()
pageController.delegate = self
pageController.dataSource = self
let buttonsVC = ButtonsViewController()
let displayMaplessVC = DisplayMaplessViewController()
buttonsVC.buttonsDelegate = displayMaplessVC
workoutViewControllers.append(buttonsVC)
workoutViewControllers.append(displayMaplessVC)
self.addChild(self.pageController)
self.view.addSubview(self.pageController.view)
self.pageController.setViewControllers([displayMaplessVC], direction: .forward, animated: true, completion: nil)
self.pageController.didMove(toParent: self)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
pageController.view.frame = view.bounds
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = workoutViewControllers.firstIndex(of: viewController) else {
return nil
}
let previousIndex = viewControllerIndex - 1
guard previousIndex >= 0 else {
return workoutViewControllers.last
}
guard workoutViewControllers.count > previousIndex else {
return nil
}
return workoutViewControllers[previousIndex]
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = workoutViewControllers.firstIndex(of: viewController) else {
return nil
}
let nextIndex = viewControllerIndex + 1
let workoutViewControllersCount = workoutViewControllers.count
guard workoutViewControllersCount != nextIndex else {
return workoutViewControllers.first
}
guard workoutViewControllersCount > nextIndex else {
return nil
}
return workoutViewControllers[nextIndex]
}
func presentationCount(for pageViewController: UIPageViewController) -> Int {
return workoutViewControllers.count
}
}
.
protocol ButtonsViewDelegate: class {
func onButtonPressed()
}
import UIKit
class ButtonsViewController: UIViewController {
weak var buttonsDelegate: ButtonsViewDelegate?
let button: UIButton = {
let button = UIButton()
button.backgroundColor = .red
button.addTarget(self, action: #selector(onButtonPressed), for: .touchUpInside)
return button
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(button)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
button.frame = CGRect(x: 50,
y: 50,
width: 100,
height: 100)
}
#objc private func onButtonPressed() {
buttonsDelegate?.onButtonPressed()
}
}
.
import UIKit
class DisplayMaplessViewController: UIViewController, ButtonsViewDelegate {
private let testView: UIView = {
let view = UIView()
view.backgroundColor = .blue
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(testView)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
testView.frame = view.bounds
}
internal func onButtonPressed() {
testView.backgroundColor = .green
}
}
Related
I have a UIPageViewController and several ViewControllers in it. And I want to perform a function in UIPageViewController from a ViewController using delegate protocol. But get nil in the delegate. Can anybody determine what I'm doing wrong?
PageViewController.swift
import UIKit
class PageViewController: UIPageViewController, UIPageViewControllerDelegate, UIPageViewControllerDataSource, Storyboarded {
weak var coordinator: MainCoordinator?
lazy var orderedViewControllers: [UIViewController] = {
return [self.newInstanceVC(viewController: "FirstLoadViewController"), self.newInstanceVC(viewController: "FirstLoad2ViewController")]
}()
var pageControl = UIPageControl()
override func viewDidLoad() {
super.viewDidLoad()
let storyboardVC = newInstanceVC(viewController: "FirstLoad2ViewController") as? FirstLoad2ViewController
storyboardVC?.delegate = self
self.dataSource = self
if let firstViewController = orderedViewControllers.first {
setViewControllers([firstViewController], direction: .forward, animated: true, completion: nil)
}
self.delegate = self
configurePageControl()
}
func configurePageControl() {
pageControl = UIPageControl(frame: CGRect(x: 0, y: UIScreen.main.bounds.maxY - 50, width: UIScreen.main.bounds.width, height: 50))
pageControl.numberOfPages = orderedViewControllers.count
pageControl.currentPage = 0
//pageControl.tintColor = .red
pageControl.pageIndicatorTintColor = .systemGray5
pageControl.currentPageIndicatorTintColor = .systemGray
self.view.addSubview(pageControl)
}
//return view controller by string identifier
func newInstanceVC(viewController: String) -> UIViewController {
return UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: viewController)
}
//MARK: - Setup page controllers
// view controller that appears on swiping to the -> (left)
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = orderedViewControllers.firstIndex(of: viewController) else {
return nil
}
let previousIndex = viewControllerIndex - 1
guard previousIndex >= 0 else {
//return view controller from the end if it is last index from the left side (infinit scroll)
//return orderedViewControllers.last
return nil
}
guard orderedViewControllers.count >= previousIndex else {
return nil
}
return orderedViewControllers[previousIndex]
}
// view controller that appears on swiping to the <- (right)
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = orderedViewControllers.firstIndex(of: viewController) else {
return nil
}
let nextIndex = viewControllerIndex + 1
guard orderedViewControllers.count != nextIndex else {
//return view controller from the begining if it is last index from the right side (infinit scroll)
//return orderedViewControllers.first
return nil
}
guard orderedViewControllers.count > nextIndex else {
return nil
}
return orderedViewControllers[nextIndex]
}
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
//switch the bullet of the page
let pageContentVievController = pageViewController.viewControllers![0]
self.pageControl.currentPage = orderedViewControllers.firstIndex(of: pageContentVievController)!
}
}
extension PageViewController: PageViewDelegate {
func handleButtonTap() {
coordinator?.goToMainFromPresentation()
}
}
OneOfTheViewControllers.swift
import UIKit
protocol PageViewDelegate: class {
func handleButtonTap()
}
class FirstLoad2ViewController: UIViewController, Storyboarded {
weak var coordinator: MainCoordinator?
weak var child: ChildCoordinator?
weak var delegate: PageViewDelegate?
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func toMainScreenTap(_ sender: Any) {
delegate!.handleButtonTap() //got nil for delegate here
}
}
The problem is simple, it's hiding in your viewDidLoad() method inside PageViewController
...
override func viewDidLoad() {
super.viewDidLoad()
let storyboardVC = newInstanceVC(viewController: "FirstLoad2ViewController") as? FirstLoad2ViewController
storyboardVC?.delegate = self
...
}
/// (storyboardVC) --- will be destroyed when out of viewDidLoad() scope.
So, why you getting your storyboardVC?.delegate = self nil? Because you created storyboardVC property inside viewDidLoad() method and nothing is keeping reference to it. So, at the end viewDidLoad() method it just deinitialize as well as it's PageViewDelegate delegate
Swift 4: I'm using NSNotification post and observe an object between two view controller. My issue is updating the viewController that observes given the received object.
There are two things that needs updating 1. setTitle of a button, 2. the current page on UIPageViewController. How do i update these two things?
I've worked on this the whole day today and yesterday, with no results.
I've tried using struct to do the updating and normal variables.
**YellowViewController - Observes -> Issue here**
let searchedReturnedKey = "Searched"
class YellowViewController: UIPageViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate {
let pageView = PageView()
let searched = Notification.Name(rawValue: searchedReturnedKey)
private var isAnimating = false
struct structure {
static var SearchedIndex = Int()
}
override func viewDidLoad() {
super.viewDidLoad()
dataSource = self
delegate = self
setupUIPageView()
setupNavigationBarItem()
createObserver()
}
func createObserver(){
NotificationCenter.default.addObserver(self, selector: #selector(YellowViewController.updateVerseView(notification:)), name: searched, object: nil)
}
#objc func updateVerseView(notification: NSNotification) {
print("Observed")
structure.SearchedIndex = notification.object as! Int
let chapterIndexLabel = doneModel.ChapterIndex
let verseList = doneModel.chapterVerses
let pageView = PageView()
// HERE IS MY ISSUE - booklabel.text and pageview.verses
bookLabel.text = chapterIndexLabel[structure.SearchedIndex]
pageView.verses = verseList[structure.SearchedIndex]
print(structure.SearchedIndex, "Index")
}
func setupUIPageView() {
let verseList = bibleModel.chapterVerses
let chapterIndexLabel = bibleModel.ChapterIndex
let pageViewControllers = [pageView] // Important! UIPageViewController sets ViewController in a list to enable swiping. - Understand this.
pageView.verses = verseList.first! // Important! Setups verses for first view.
bookLabel.text = chapterIndexLabel.first!
setViewControllers(pageViewControllers, direction: .forward, animated: true, completion: nil)
}
var bookLabel = UILabel()
func setupLeftNavItems() {
let bookButton = UIButton(type: .system)
bookButton.setTitle(bookLabel.text! + " 1", for: .normal)
bookButton.setTitleColor(goldColor, for: .normal)
bookButton.titleLabel?.font = UIFont(name: "AvenirNext-DemiBold", size: 18)
bookButton.addTarget(self, action: #selector(handleSearch), for: .touchUpInside)
// bookButton.frame = CGRect(x: 30, y: 0, width: 54, height: 34)
self.navigationItem.leftBarButtonItem = UIBarButtonItem(customView: bookButton)
}
#objc func handleSearch() { // Handles openeing SearchViewController
let searchViewController = SearchViewController()
let navController = UINavigationController(rootViewController: searchViewController)
present(navController, animated: true, completion: nil)
}
func pageViewController(_ pageViewController: UIPageViewController, willTransitionTo pendingViewControllers: [UIViewController]) {
isAnimating = true
}
// Handles swiping right - To next chapter.
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
if isAnimating {
return nil
}
let verseList = bibleModel.chapterVerses
let currentVerseView = (viewController as! PageView).verses
let currentIndex = verseList.index(of: currentVerseView)
let chapterIndexLabel = doneModel.ChapterIndex
if currentIndex! < verseList.count - 1 {
let pageView = PageView()
pageView.verses = verseList[currentIndex! + 1 ]
bookLabel.text = chapterIndexLabel[currentIndex! + 1]
print("Swiped right")
setupLeftNavItems()
createObserver()
return pageView
}
return nil
}
// Handles swiping left - To previous chapter
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
if isAnimating {
return nil
}
let verseList = bibleModel.chapterVerses
let currentVerseView = (viewController as! PageView).verses
let currentIndex = verseList.index(of: currentVerseView)
let chapterIndexLabel = doneModel.ChapterIndex
if currentIndex! > 0 {
let pageView = PageView()
pageView.verses = verseList[currentIndex! - 1]
bookLabel.text = chapterIndexLabel[currentIndex! - 1]
setupLeftNavItems()
print("Swiped left")
createObserver()
return pageView
}
return nil
}
}
SearchViewController - Post
class SearchViewController: UITableViewController, UISearchBarDelegate {
let cellId = "cellId"
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.titleView = navSearchBar
setupView()
}
func setupView() {
tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellId)
tableView.delegate = self
tableView.dataSource = self
navSearchBar.delegate = self
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return Books.count
}
**I post the data from this function**
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath)
let cellLabelContent = cell!.textLabel!.text
let cellLabelIndex = Books.firstIndex(of: cellLabelContent!)
print("Book name:", cellLabelContent!, "index:", cellLabelIndex!)
let notificationName = Notification.Name(rawValue: searchedReturnedKey)
NotificationCenter.default.post(name: notificationName, object: cellLabelIndex)
dismiss(animated: true, completion: nil)
}
}
Actual result -> No update
Expected result -> Update given object
To be able to update the current view of your UIPageViewController, you would need to do this inside your updateVerseView. The setViewControllers method is correct and efficient method to update a view inside a UIPageViewController.
let pageViewControllers = [pageView]
pageView.verses = verseList[structure.searchedIndex]
setViewControllers(pageViewControllers, direction: .forward, animated: true, completion: nil)
To update your bookLabel.txt you would just need to call your setupLeftNavItems() function.
Here you need toaddObserver with your selector #selector(YellowViewController.updateVerseView(notification:)
I think you set wrong ViewController name
I created a PageController with 3 pages .. I allocated and declared everything, I implemented the two functions that allow scrolling views, in the same I entered the commands to enable the controller with the right page reported. the result is that if I flow my pages is all ok, while under the page counter works as he wants.. and I can not understand what is wrong
problemproblem2problem3
import UIKit
import AVFoundation
protocol IntroNavigationDelegate: class {
func showNextViewController()
func showPreviousViewController()
var isPagingEnabled: Bool { get set }
}
final class IntroViewController: UIPageViewController, UIPageViewControllerDelegate, IntroNavigationDelegate, UIPageViewControllerDataSource {
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
if let viewControllerIndex = self.displayedControllers.index(of: viewController) {
if viewControllerIndex == 0 {
} else if viewControllerIndex == 1 {
return self.displayedControllers[viewControllerIndex - 1]
}
}
return nil
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
if let viewControllerIndex = self.displayedControllers.index(of: viewController) {
if viewControllerIndex < self.displayedControllers.count - 1 {
self.pageControl.currentPage = 1
return self.displayedControllers[viewControllerIndex + 1]
} else {
self.pageControl.currentPage = 0
}
}
return nil
}
var introRouter: IntroRouter?
var pageControl = UIPageControl()
var displayedControllers: [UIViewController] = []
private var scrollView: UIScrollView? {
for view in view.subviews {
if let subView = view as? UIScrollView {
return subView
}
}
return nil
}
var isPagingEnabled: Bool {
get {
return scrollView?.isScrollEnabled ?? true
}
set {
scrollView?.isScrollEnabled = newValue
}
}
override func viewDidLoad() {
super.viewDidLoad()
configurePageControl()
setupUI()
self.pageControl.updateCurrentPageDisplay()
arrangeSubviews()
self.delegate = self
}
override func viewWillAppear(_ animated: Bool) {
self.navigationController?.setNavigationBarHidden(true, animated: animated)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
navigationController?.isNavigationBarHidden = false
}
func setDiplayedControllers(_ controllers: [UIViewController], visualizedController: UIViewController) {
displayedControllers = controllers
setViewControllers([visualizedController], direction: .forward, animated: false, completion: nil)
}
func showNextViewController() {
if let current = viewControllers?.first, let next = IntroViewController(self, viewControllerAfter: current) {
setViewControllers([next], direction: .forward, animated: true, completion: nil)
}
}
func showPreviousViewController() {
if let current = viewControllers?.first, let previous = IntroViewController(self, viewControllerBefore: current) {
setViewControllers([previous], direction: .reverse, animated: true, completion: nil)
}
}
func configurePageControl(){
self.pageControl.frame = CGRect()
self.pageControl.numberOfPages = self.displayedControllers.count
self.pageControl.translatesAutoresizingMaskIntoConstraints = false
self.pageControl.updateCurrentPageDisplay()
self.view.addSubview(self.pageControl)
pageControl.activate([
pageControl.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: -20),
pageControl.heightAnchor.constraint(equalTo: self.view.heightAnchor, multiplier: 0.1),
pageControl.widthAnchor.constraint(equalTo: self.view.widthAnchor, multiplier: 1),
])
}
override init(transitionStyle style: UIPageViewControllerTransitionStyle, navigationOrientation: UIPageViewControllerNavigationOrientation, options: [String : Any]? = nil) {
super.init(transitionStyle: style, navigationOrientation: navigationOrientation, options: options)
setup()
}
func setRouter(introRouter: IntroRouter) {
self.introRouter = introRouter
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setup() {
view.backgroundColor = .white
dataSource = self
}
}
private extension IntroViewController {
func IntroViewController(_ IntroViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
if let viewControllerIndex = self.displayedControllers.index(of: viewController) {
if viewControllerIndex == 0 {
// wrap to last page in array
return self.displayedControllers.last
} else {
// go to previous page in array
return self.displayedControllers[viewControllerIndex + 1]
}
}
return nil
}
func IntroViewController(_ IntroViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
self.pageControl.updateCurrentPageDisplay()
if let viewControllerIndex = self.displayedControllers.index(of: viewController) {
if viewControllerIndex < self.displayedControllers.count - 1 {
// go to next page in array
self.pageControl.currentPage = viewControllerIndex
return self.displayedControllers[viewControllerIndex + 1]
} else {
// wrap to first page in array
self.pageControl.currentPage = viewControllerIndex
return self.displayedControllers.first
}
}
return nil
}
func IntroViewController(_ IntroViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
// set the pageControl.currentPage to the index of the current viewController in pages
if let viewControllers = IntroViewController.viewControllers {
if let viewControllerIndex = self.displayedControllers.index(of: viewControllers[0]) {
self.pageControl.currentPage = viewControllerIndex
self.pageControl.updateCurrentPageDisplay()
}
}
}
func setupUI() {
configurePageControl()
pageControl.do {
$0.numberOfPages = 3
// $0.currentPage = 0
$0.pageIndicatorTintColor = .lightGray
$0.currentPageIndicatorTintColor = Theme.Colors.white
$0.currentPage = self.displayedControllers.count
}
}
func arrangeSubviews() {
view.addSubview(pageControl)
}
}
Just try the below code. It would be helpfull.
class PageViewModel: NSObject {
private var timer: Timer?
private var interval: Double?
var enableAutoScroll: Bool = false {
didSet {
self.toggleTimer()
}
}
var viewControllers: [UIViewController]
var currentIndex: Int = 0
var direction: UIPageViewControllerNavigationDirection = .forward
init(viewControllers: [UIViewController]) {
self.viewControllers = viewControllers
super.init()
}
override convenience init() {
self.init(viewControllers: [])
}
private func stopTimer() {
if self.timer != nil {
timer?.invalidate()
timer = nil
return
}
}
private func startTimer() {
if self.timer != nil {
return
}
if #available(iOS 10.0, *) {
if let time = interval {
timer = Timer.scheduledTimer(withTimeInterval: time, repeats: true, block: { (timer: Timer) in
self.timerFire()
})
}
} else {
// Fallback on earlier versions
if let time = interval {
timer = Timer.scheduledTimer(timeInterval: time, target: self, selector: #selector(PageViewModel.timerFire), userInfo: nil, repeats: true)
}
}
}
#objc private func timerFire() {
if direction == .forward {
var index = currentIndex
if index < self.viewControllers.count - 1 {
index += 1
} else {
index = 0
}
currentIndex = index
scrollToNextPage?(currentIndex)
pageNumberChangedObserver?(currentIndex)
} else {
var index = currentIndex
if index <= 0 {
index = (self.viewControllers.count - 1)
} else {
index -= 1
}
currentIndex = index
scrollToNextPage?(index)
pageNumberChangedObserver?(currentIndex)
}
}
// MARK: - Timer
func toggleTimer() {
if enableAutoScroll == true {
startTimer()
} else {
stopTimer()
}
}
fileprivate var scrollToNextPage: ((_ index: Int) -> Void)?
func scrollToNextPage(callBack: #escaping (_ index: Int) -> Void) {
scrollToNextPage = callBack
}
// MARK: - PageNumberChangedObserver
fileprivate var pageNumberChangedObserver: ((_ index: Int) -> Void)?
func pageNumberChanged(callBack: #escaping (_ index: Int) -> Void) {
pageNumberChangedObserver = callBack
}
}
// MARK: - UIPageViewControllerDataSource
extension PageViewModel: UIPageViewControllerDataSource {
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = viewControllers.index(of: viewController) else {
return nil
}
let previousIndex = viewControllerIndex - 1
// User is on the first view controller and swiped left to loop to
// the last view controller.
guard previousIndex >= 0 else {
//return orderedViewControllers.last
// Uncommment the line below, remove the line above if you don't want the page control to loop.
return nil
}
guard viewControllers.count > previousIndex else {
return nil
}
return viewControllers[previousIndex]
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = viewControllers.index(of: viewController) else {
return nil
}
let nextIndex = viewControllerIndex + 1
let orderedViewControllersCount = viewControllers.count
// User is on the last view controller and swiped right to loop to
// the first view controller.
guard orderedViewControllersCount != nextIndex else {
//return orderedViewControllers.first
// Uncommment the line below, remove the line above if you don't want the page control to loop.
return nil
}
guard orderedViewControllersCount > nextIndex else {
return nil
}
return viewControllers[nextIndex]
}
}
// MARK: - UIPageViewControllerDelegate
extension PageViewModel: UIPageViewControllerDelegate {
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
if let pageContentViewController = pageViewController.viewControllers?[0] {
if let index = viewControllers.index(of: pageContentViewController) {
currentIndex = index
pageNumberChangedObserver?(currentIndex)
}
}
}
}
Write below code in your Container View Controller: -
// set current page to the page controller
func setNextPage(index: Int, direction: UIPageViewControllerNavigationDirection) {
if let firstViewController = pageViewModel?.viewControllers[selectedCategoryCell] {
pageViewController?.setViewControllers([firstViewController],
direction: direction,
animated: true,
completion: nil)
}
}
// MARK: - SET PAGE CONTROLLER's VIEW's
private func setupPageViewController() {
let pageViewController: UIPageViewController = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil)
pageViewController.dataSource = pageViewModel
pageViewController.delegate = pageViewModel
if let firstViewController = pageViewModel?.viewControllers[selectedCategoryCell] {
pageViewController.setViewControllers([firstViewController],
direction: .forward,
animated: true,
completion: nil)
}
for recognizer in pageViewController.gestureRecognizers {
if recognizer is UITapGestureRecognizer {
recognizer.isEnabled = false
}
}
self.addChildViewController(pageViewController)
self.containerView.addSubview(pageViewController.view)
var pageViewRect = self.containerView.bounds
if UIDevice.current.userInterfaceIdiom == .pad {
pageViewRect = pageViewRect.insetBy(dx: 40.0, dy: 40.0)
}
pageViewController.view.frame = pageViewRect
pageViewController.didMove(toParentViewController: self)
self.pageViewController = pageViewController
}
// set the selected page and accordingly set the above collection view ( that is treatment option's collection view)
func pageNumberChanged(index: Int) {
self.selectedCategoryCell = index
}
// MARK: Init View Controller for Page Controller
func initViewController(index: Int) -> UIViewController {
guard let itemsViewController = self.storyboard?.instantiateViewController(withIdentifier: "MatchesV2ViewController") as? MatchesV2ViewController else {
fatalError("Items View Controller Not Found")
}
itemsViewController.delegate = self
itemsViewController.currentMatch = matches.user[index]
return itemsViewController
}
// MARK: Setting VC In Page View Controller
func instantiateViewControllers() {
matchesScreen.removeAll()
for i in 0..<matches.user.count {
matchesScreen.append(initViewController(index: i))
}
pageViewModel = PageViewModel(viewControllers: matchesScreen)
pageViewModel?.pageNumberChanged(callBack: { (index: Int) in
self.pageNumberChanged(index: index)
})
pageViewModel?.scrollToNextPage(callBack: { (index: Int) in
self.setNextPage(index: index, direction: .forward)
})
if matchesScreen.count > 0 {
self.setupPageViewController()
}
}
I wrote this code that checked several times I can not understand where I'm wrong.
I can not see my page..
but I see a black background with the 3 dots of pageControl.
I checked several times, I set the yellow background on the first page..
can you give me a hand?
I have 3-4 more files in which I declare the pages and their layout
Result
final class IntroViewController: UIPageViewController, UIPageViewControllerDelegate {
var introRouter: IntroRouter?
var pages = [UIViewController]()
let pageControl = UIPageControl()
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
// self.delegate = self
let initialPage = 1
let page1 = ViewController1()
let page2 = ViewController2()
let page3 = ViewController3()
let page4 = ViewController4()
self.pages.append(page1)
self.pages.append(page2)
self.pages.append(page3)
self.pages.append(page4)
setViewControllers([pages[initialPage]], direction: .forward, animated: true, completion: nil)
self.pageControl.frame = CGRect()
self.pageControl.currentPageIndicatorTintColor = UIColor.black
self.pageControl.pageIndicatorTintColor = UIColor.lightGray
self.pageControl.numberOfPages = self.pages.count
self.pageControl.currentPage = initialPage
self.view.addSubview(self.pageControl)
self.pageControl.translatesAutoresizingMaskIntoConstraints = false
self.pageControl.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: -5).isActive = true
self.pageControl.widthAnchor.constraint(equalTo: self.view.widthAnchor, constant: -20).isActive = true
self.pageControl.heightAnchor.constraint(equalToConstant: 20).isActive = true
self.pageControl.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
}
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
arrangeSubviews()
}
override init(transitionStyle style: UIPageViewControllerTransitionStyle, navigationOrientation: UIPageViewControllerNavigationOrientation, options: [String : Any]? = nil) {
super.init(transitionStyle: style, navigationOrientation: navigationOrientation, options: options)
}
func setRouter(introRouter: IntroRouter) {
self.introRouter = introRouter
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
private extension IntroViewController {
func IntroViewController(_ IntroViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
if let viewControllerIndex = self.pages.index(of: viewController) {
if viewControllerIndex == 0 {
// wrap to last page in array
return self.pages.last
} else {
// go to previous page in array
return self.pages[viewControllerIndex - 1]
}
}
return nil
}
func IntroViewController(_ IntroViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
if let viewControllerIndex = self.pages.index(of: viewController) {
if viewControllerIndex < self.pages.count - 1 {
// go to next page in array
return self.pages[viewControllerIndex + 1]
} else {
// wrap to first page in array
return self.pages.first
}
}
return nil
}
func IntroViewController(_ IntroViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
// set the pageControl.currentPage to the index of the current viewController in pages
if let viewControllers = IntroViewController.viewControllers {
if let viewControllerIndex = self.pages.index(of: viewControllers[0]) {
self.pageControl.currentPage = viewControllerIndex
}
}
}
func setupUI() {
pageControl.do {
$0.numberOfPages = 4
$0.currentPage = 1
$0.pageIndicatorTintColor = .lightGray
$0.currentPageIndicatorTintColor = Theme.Colors.white
}
}
func arrangeSubviews() {
view.addSubview(pageControl)
}
}
I am displaying images in a collection view controller. When the cell is tapped, I am passing those images to page view controller, where the user is given an option to delete or add image description as you can see in the below images.
When the user clicks delete button, I would the page (or view controller) to be deleted (just like the behaviour seen, when delete button is clicked in in Apple iOS photos app).
I tried to achieve it, by passing an array of empty view controller to pageViewController (See Code A), which resulted in a error
The number of view controllers provided (0) doesn't match the number required (1) for the requested transition which makes sense.
Am I on the right track, if yes, how can I fix the issue ?
If not, Is there a better approach to achieve the same ?
Code A: Taken from Code B
pageVC.setViewControllers([], direction: .forward, animated: true, completion: nil)
Code B: Taken from UserPickedImageVC
func deleteCurrentImageObject(){
guard let controllers = self.navigationController?.viewControllers else{
return
}
for viewController in controllers {
if viewController.className == "UserPickedImagesVC"{
let vc = viewController as! UserPickedImagesVC
let objectCount = vc.imageObjectsArray.count
guard objectCount > 0 && objectCount >= itemIndex else {
return
}
vc.imageObjectsArray.remove(at: itemIndex) // Removing imageObject from the array
if let pageVC = vc.childViewControllers[0] as? UIPageViewController {
pageVC.setViewControllers([], direction: .forward, animated: true, completion: nil)
}
}
}
}
Storyboard
Here is the complete code (except some custom UICollectionViewCell):
UserPickedImagesCVC.swift
import UIKit
import ImagePicker
import Lightbox
private let imageCellId = "imageCell"
private let addCellId = "addImagesCell"
class UserPickedImagesCVC: UICollectionViewController, ImagePickerDelegate, UserPickedImagesVCProtocol {
let imagePickerController = ImagePickerController()
//var userPickedImages = [UIImage]()
var userPickedImages = [ImageObject]()
override func viewDidLoad() {
super.viewDidLoad()
imagePickerController.delegate = self as ImagePickerDelegate
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Register cell classes
self.collectionView!.register(ImageCVCell .self, forCellWithReuseIdentifier: imageCellId)
self.collectionView!.register(ImagePickerButtonCVCell.self, forCellWithReuseIdentifier: addCellId)
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
// MARK: UICollectionViewDataSource
override func numberOfSections(in collectionView: UICollectionView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of items
return userPickedImages.count + 1
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let item = indexPath.item
print("item: \(item)")
if item < userPickedImages.count {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: imageCellId, for: indexPath) as! ImageCVCell
let userPickedImageObject = userPickedImages[item]
//cell.showImagesButton.setImage(userPickedImage, for: .normal)
cell.showImagesButton.setImage(userPickedImageObject.image, for: .normal)
cell.showImagesButton.addTarget(self, action: #selector(showAlreadyPickedImages), for: .touchUpInside)
//cell.addButton.addTarget(self, action: #selector(showAlreadyPickedImages), for: .touchUpInside)
return cell
}
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: addCellId, for: indexPath) as! ImagePickerButtonCVCell
cell.addButton.addTarget(self, action: #selector(showImagePickerController), for: .touchUpInside)
return cell
// Configure the cell
}
//Function shows imagePicker that helps in picking images and capturing images with camera
func showImagePickerController(){
print("showImagePickerController func called")
//self.present(imagePickerController, animated: true, completion: nil)
self.navigationController?.pushViewController(imagePickerController, animated: true)
}
func showAlreadyPickedImages(){
let vc = self.storyboard?.instantiateViewController(withIdentifier: "userPickedImagesVC") as! UserPickedImagesVC
//vc.contentImages = userPickedImages
vc.imageObjectsArray = userPickedImages
vc.showingAlreadySavedImages = true
self.navigationController?.pushViewController(vc, animated: true)
}
func setImagesInCells(imageObjects : [ImageObject]){
print("setImagesInCells func called in CVC")
userPickedImages += imageObjects
collectionView?.reloadData()
}
// MARK: - ImagePickerDelegate
func cancelButtonDidPress(_ imagePicker: ImagePickerController) {
imagePicker.dismiss(animated: true, completion: nil)
}
func wrapperDidPress(_ imagePicker: ImagePickerController, images: [UIImage]) {
guard images.count > 0 else { return }
let lightboxImages = images.map {
return LightboxImage(image: $0)
}
let lightbox = LightboxController(images: lightboxImages, startIndex: 0)
imagePicker.present(lightbox, animated: true, completion: nil)
}
func doneButtonDidPress(_ imagePicker: ImagePickerController, images: [UIImage]) {
imagePicker.dismiss(animated: true, completion: nil)
let vc = storyboard?.instantiateViewController(withIdentifier: "userPickedImagesVC") as! UserPickedImagesVC
//vc.contentImages = images
vc.imageObjectsArray = convertImagesToImageObjects(images)
//self.present(vc, animated: true, completion: nil)
self.navigationController?.pushViewController(vc, animated: true)
}
func convertImagesToImageObjects(_ imagesArray : [UIImage]) -> [ImageObject]{
var imageObjects = [ImageObject]()
for image in imagesArray{
var imageObject = ImageObject()
imageObject.image = image
imageObject.imageDescription = ""
imageObjects.append(imageObject)
}
return imageObjects
}
}
UserPickedImagesVC.swift
import UIKit
protocol UserPickedImagesVCProtocol{
func setImagesInCells(imageObjects : [ImageObject])
}
class ImageObject : NSObject{
var imageDescription : String?
var image : UIImage?
}
class UserPickedImagesVC: UIViewController, UIPageViewControllerDataSource {
var pageViewController : UIPageViewController?
let placeholderText = "Image description.."
var imageObjectsArray = [ImageObject]()
var delegate : UserPickedImagesVCProtocol!
var showingAlreadySavedImages = false
override func viewDidLoad() {
super.viewDidLoad()
edgesForExtendedLayout = [] // To avoid view going below nav bar
//self.delegate = self.navigationController?.viewControllers
// Do any additional setup after loading the view, typically from a nib.
if showingAlreadySavedImages{
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Done", style: .plain, target: self, action: #selector(doneTapped))
}else{
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Save", style: .plain, target: self, action: #selector(saveTapped))
}
// createImageAndDescriptionDict()
createPageViewController()
setupPageControl()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func createPageViewController(){
print("createPageViewController func called")
let pageController = self.storyboard?.instantiateViewController(withIdentifier: "userPickedImagesPageController") as! UIPageViewController
pageController.dataSource = self
if imageObjectsArray.count > 0 {
let firstController = getItemController(0)
let startingViewControllers = [firstController]
pageController.setViewControllers(startingViewControllers as! [UIViewController], direction: .forward, animated: false, completion: nil)
}
pageViewController = pageController
addChildViewController(pageViewController!)
self.view.addSubview((pageViewController?.view)!)
pageViewController?.didMove(toParentViewController: self)
}
// Creata the appearance of pagecontrol
func setupPageControl(){
let appearance = UIPageControl.appearance()
appearance.pageIndicatorTintColor = UIColor.gray
appearance.currentPageIndicatorTintColor = UIColor.white
appearance.backgroundColor = UIColor.darkGray
}
//MARK: Delagate methods
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
let itemController = viewController as! UserPickedImageVC
if itemController.itemIndex > 0 {
return self.getItemController(itemController.itemIndex-1)
}
return nil
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
let itemController = viewController as! UserPickedImageVC
if itemController.itemIndex + 1 < imageObjectsArray.count{
return getItemController(itemController.itemIndex+1)
}
return nil
}
func presentationCount(for pageViewController: UIPageViewController) -> Int {
return imageObjectsArray.count
}
func presentationIndex(for pageViewController: UIPageViewController) -> Int {
return 0
}
func currentControllerIndex() -> Int{
let pageItemController = self.currentControllerIndex()
if let controller = pageItemController as? UserPickedImageVC{
return controller.itemIndex
}
return -1
}
func currentController() -> UIViewController?{
if(self.pageViewController?.viewControllers?.count)! > 0{
return self.pageViewController?.viewControllers?[0]
}
return nil
}
func getItemController(_ itemIndex:Int) -> UserPickedImageVC?{
if itemIndex < imageObjectsArray.count{
let pageItemController = self.storyboard?.instantiateViewController(withIdentifier: "userPickedImageVC") as! UserPickedImageVC
pageItemController.itemIndex = itemIndex
//pageItemController.imageName = imageObjectsArray[itemIndex]
//pageItemController.imageToShow = imageObjectsArray[itemIndex]
//pageItemController.imageToShow = getImageFromImageDescriptionArray(itemIndex, imagesAndDescriptionArray)
pageItemController.imageObject = imageObjectsArray[itemIndex]
pageItemController.itemIndex = itemIndex
pageItemController.showingAlreadySavedImage = showingAlreadySavedImages
print("Image Name from VC: \(imageObjectsArray[itemIndex])")
return pageItemController
}
return nil
}
// Passing images back to Collection View Controller when save button is tapped
func saveTapped(){
let viewControllers = self.navigationController?.viewControllers
//print("viewControllers: \(viewControllers)")
if let destinationVC = viewControllers?[0]{
self.delegate = destinationVC as! UserPickedImagesVCProtocol
//self.delegate.setImagesInCells(images : imageObjectsArray)
self.delegate.setImagesInCells(imageObjects : imageObjectsArray)
self.navigationController?.popToViewController(destinationVC, animated: true)
}
}
func doneTapped(){
let viewControllers = self.navigationController?.viewControllers
if let destinationVC = viewControllers?[0] {
self.navigationController?.popToViewController(destinationVC, animated: true)
}
}
}
UserPickedImageVC.swift
import UIKit
import ImageScrollView
extension UIViewController {
var className: String {
return NSStringFromClass(self.classForCoder).components(separatedBy: ".").last!;
}
}
class UserPickedImageVC: UIViewController, UITextViewDelegate {
var itemIndex : Int = 0
var imageDescription : String = ""
var imageScrollView = ImageScrollView()
var imageDescriptionTextView : UITextView!
var imageToShow : UIImage!
var imageObject : ImageObject?
var deleteButton = UIButton(type: .system)
var showingAlreadySavedImage = false
var pageViewController : UIPageViewController!
override func viewDidLoad() {
super.viewDidLoad()
edgesForExtendedLayout = [] // To avoid images going below the navigation bars
pageViewController = self.parent as! UIPageViewController
setConstraints()
setImageAndDescription()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
//MARK: TextView delegate methods
func textViewDidBeginEditing(_ textView: UITextView)
{
if (imageDescriptionTextView.text == "Image description..")
{
imageDescriptionTextView.text = ""
imageDescriptionTextView.textColor = .black
}
imageDescriptionTextView.becomeFirstResponder() //Optional
}
func textViewDidEndEditing(_ textView: UITextView)
{
let imageDescription = imageDescriptionTextView.text
if (imageDescription == "")
{
imageDescriptionTextView.text = "Image description.."
imageDescriptionTextView.textColor = .lightGray
}
imageObject?.imageDescription = imageDescription
updateImageObject(imageObject!)
imageDescriptionTextView.resignFirstResponder()
}
//MARK: Private Methods
func setImageAndDescription(){
if let imageToDisplay = imageObject?.image{
imageScrollView.display(image: imageToDisplay) // Setting Image
}
imageDescriptionTextView.text = imageObject?.imageDescription // Setting Description
}
// Function to update imageObject in UserPickedImagesVC
func updateImageObject(_ imageObject: ImageObject){
guard let controllers = self.navigationController?.viewControllers else{
return
}
for viewController in controllers {
if viewController.className == "UserPickedImagesVC" {
let vc = viewController as! UserPickedImagesVC
vc.imageObjectsArray[itemIndex] = imageObject
}
}
}
// Function to delete imageObject from UserPickedImagesVC
func deleteCurrentImageObject(){
guard let controllers = self.navigationController?.viewControllers else{
return
}
for viewController in controllers {
if viewController.className == "UserPickedImagesVC"{
let vc = viewController as! UserPickedImagesVC
let objectCount = vc.imageObjectsArray.count
guard objectCount > 0 && objectCount >= itemIndex else {
return
}
vc.imageObjectsArray.remove(at: itemIndex) // Removing imageObject from the array
if let pageVC = vc.childViewControllers[0] as? UIPageViewController {
pageVC.setViewControllers([], direction: .forward, animated: true, completion: nil)
}
}
}
}
func showOrHideDeleteButton(){
if showingAlreadySavedImage{
print("deleteButton.isNotHidden")
deleteButton.isHidden = false
}else{
print("deleteButton.isHidden")
deleteButton.isHidden = true
}
}
func setConstraints(){
let viewSize = self.view.frame.size
let viewWidth = viewSize.width
let viewHeight = viewSize.height
print("viewWidth: \(viewWidth), viewHeight: \(viewHeight)")
view.addSubview(imageScrollView)
imageScrollView.frame = CGRect(x: 0, y: 0, width: viewWidth, height: viewHeight)
deleteButton.tintColor = Colors.iOSBlue
deleteButton.setImage(#imageLiteral(resourceName: "delete"), for: .normal)
deleteButton.backgroundColor = Colors.white
deleteButton.layer.cornerRadius = 25
deleteButton.imageEdgeInsets = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
//deleteButton.currentImage.
deleteButton.imageView?.tintColor = Colors.iOSBlue
deleteButton.addTarget(self, action: #selector(deleteCurrentImageObject), for: .touchUpInside)
deleteButton.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(deleteButton)
showOrHideDeleteButton()
imageDescriptionTextView = UITextView()
imageDescriptionTextView.delegate = self as! UITextViewDelegate
imageDescriptionTextView.text = "Image description.."
imageDescriptionTextView.textColor = .lightGray
//imageScrollView.clipsToBounds = true
imageDescriptionTextView.translatesAutoresizingMaskIntoConstraints = false
//imageDescriptionTextView.backgroundColor = UIColor.white.withAlphaComponent(0.8)
imageDescriptionTextView.backgroundColor = UIColor.white
imageDescriptionTextView.layer.cornerRadius = 5
imageDescriptionTextView.layer.borderColor = UIColor.lightGray.cgColor
imageDescriptionTextView.layer.borderWidth = 0.5
view.addSubview(imageDescriptionTextView)
let viewsDict = [
"imageDescriptionTextView" : imageDescriptionTextView,
"deleteButton" : deleteButton
] as [String:Any]
view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-5-[imageDescriptionTextView]-70-|", options: [], metrics: nil, views: viewsDict))
view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:[imageDescriptionTextView(50)]-5-|", options: [], metrics: nil, views: viewsDict))
imageDescriptionTextView.sizeThatFits(CGSize(width: imageDescriptionTextView.frame.size.width, height: imageDescriptionTextView.frame.size.height))
view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:[deleteButton(50)]-5-|", options: [], metrics: nil, views: viewsDict))
view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:[deleteButton(50)]-5-|", options: [], metrics: nil, views: viewsDict))
}
}
create a call back property in UserPickedImageVC.swift
typealias DeleteCallBack = (int) -> Void
...
var itemIndex : Int = 0
var deleteCallBack:DeleteCallBack?
...
func deleteCurrentImageObject(){
self.deleteCallBack?(self.itemIndex)
}
in UserPickedImagesVC.swift
func getItemController(_ itemIndex:Int) -> UserPickedImageVC?{
if itemIndex < imageObjectsArray.count{
let pageItemController = self.storyboard?.instantiateViewController(withIdentifier: "userPickedImageVC") as! UserPickedImageVC
pageItemController.itemIndex = itemIndex
pageItemController.imageObject = imageObjectsArray[itemIndex]
pageItemController.itemIndex = itemIndex
pageItemController.showingAlreadySavedImage = showingAlreadySavedImages
print("Image Name from VC: \(imageObjectsArray[itemIndex])")
pageItemController.deleteCallBack = {
[weak self] (index) -> Void in
self?.deleteItemAt(index: index)
}
return pageItemController
}
return nil
}
func deleteItemAt(index: Int) {
if (imageObjectsArray.count > 1) {
imageObjectsArray.remove(at: itemIndex)
self.pageViewController.dataSource = nil;
self.pageViewController.dataSource = self;
let firstController = getItemController(0)
let startingViewControllers = [firstController]
pageViewController.setViewControllers(startingViewControllers as! [UIViewController], direction: .forward, animated: false, completion: nil)
} else {
//redirect here
_ = navigationController?.popViewController(animated: true)
}
}