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
Related
Is it possible to reverse the animation direction for UIPageViewController for right-to-left languages when using UIPageViewController.TransitionStyle.scroll?
Swiping left and right works inverts correctly for right-to-left, but my next button solution animates in the wrong direction (animates like it does for left-to-right).
My current solution to do so is to set pageControl.semanticContentAttribute = .forceLeftToRight, and to inverse the viewModels so that index 0 for right-to-left is the last index (viewModels.count - 1), as well as inverting the animation direction + viewControllerBefore/viewControllerAfter, e.g.:
viewControllerBefore becomes: nextIndex = isRightToLeft ? vc.index - 1 : vc.index + 1(and vice-versa for viewControllerAfter)
nextTapped's inDirection parameter for transitionFrom changes to isRightToLeft ? .reverse : .forward
Change loadFirstPage to set the startIndex to numberOfPages - 1 (aka viewModels.count - 1) rather than 0
Reverse the view models so index 0 is viewModels.count - 1 (in init):
if isRightToLeft {
viewModels = viewModels.map({ viewModel in
ViewModel(index: (numberOfPages - viewModel.index - 1), text: viewModel.text, color: viewModel.color)
}).reversed()
But I would like a solution that simply changes the animation direction out-of-the-box, like it seems to be possible when changing the spineLocation for UIPageViewController.TransitionStyle.pageCurl, like this question states.
Animations
Note that for the How it is by default the page comes from the right, but the page control indicator moves in the opposite (expected) direction.
How it is by default
Expected Functionality
Default Implementation:
import UIKit
struct ViewModel {
var index: Int
var text: String
var color: UIColor
}
class DummyViewController : UIViewController {
let label = UILabel()
let vm: ViewModel
let index: Int
init(vm: ViewModel) {
self.vm = vm
self.index = vm.index
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
override func loadView() {
let view = UIView()
view.backgroundColor = vm.color
let label = UILabel()
label.frame = CGRect(x: 20, y: 200, width: 200, height: 20)
label.textColor = .black
label.text = vm.text
view.addSubview(label)
self.view = view
}
}
class ViewController : UIViewController, UIPageViewControllerDelegate, UIPageViewControllerDataSource {
let pageViewController = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil)
let pageControl = UIPageControl()
let nextButton = UIButton()
var isRightToLeft: Bool {
traitCollection.layoutDirection == .rightToLeft
}
var currentIndex: Int = 0 {
didSet {
pageControl.currentPage = currentIndex
}
}
var numberOfPages: Int { return viewModels.count }
var viewModels: [ViewModel] = [
ViewModel(index: 0, text: "First", color: .red),
ViewModel(index: 1, text: "Second", color: .blue),
ViewModel(index: 2, text: "Third", color: .green),
]
required init?(coder: NSCoder) {
super.init(coder: coder)
pageViewController.dataSource = self
pageViewController.delegate = self
pageControl.currentPage = currentIndex
pageControl.numberOfPages = numberOfPages
nextButton.setTitle("Next", for: .normal)
nextButton.setTitleColor(.black, for: .normal)
}
override func viewDidLoad() {
addChild(pageViewController)
pageViewController.didMove(toParent: self)
view.addSubview(pageViewController.view)
view.addSubview(pageControl)
view.addSubview(nextButton)
view.subviews.forEach { $0.translatesAutoresizingMaskIntoConstraints = false }
NSLayoutConstraint.activate([
pageViewController.view.topAnchor.constraint(equalTo: view.topAnchor),
pageViewController.view.leftAnchor.constraint(equalTo: view.leftAnchor),
pageViewController.view.rightAnchor.constraint(equalTo: view.rightAnchor),
pageViewController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor),
pageControl.centerXAnchor.constraint(equalTo: view.centerXAnchor),
pageControl.centerYAnchor.constraint(equalTo: nextButton.centerYAnchor),
nextButton.trailingAnchor.constraint(equalTo: view.trailingAnchor),
nextButton.bottomAnchor.constraint(equalTo: view.layoutMarginsGuide.bottomAnchor)
])
nextButton.addTarget(self, action: #selector(nextTapped), for: .primaryActionTriggered)
loadFirstPage()
}
func loadFirstPage() {
let startIndex: Int
startIndex = 0
guard let start = viewControllerAtIndex(startIndex) else {
return
}
pageViewController.setViewControllers([start], direction: .forward, animated: true, completion: nil)
}
func transitionFrom(index: Int, inDirection direction: UIPageViewController.NavigationDirection) {
let nextIndex = direction == .forward ? index + 1 : index - 1
guard let next = viewControllerAtIndex(nextIndex) else {
return
}
pageViewController.setViewControllers([next], direction: direction, animated: true, completion: { finished in
self.pageViewController(self.pageViewController, didFinishAnimating: finished, previousViewControllers: [], transitionCompleted: finished)
})
}
func viewControllerAtIndex(_ index: Int) -> DummyViewController? {
guard index >= 0 && index < numberOfPages else { return nil }
let viewModel = viewModels[index]
return DummyViewController(vm: viewModel)
}
#objc
func nextTapped(_ sender: UIButton) {
guard currentIndex < numberOfPages - 1 else {
print("ending because \(currentIndex)")
return
}
let direction: UIPageViewController.NavigationDirection = .forward
transitionFrom(index: currentIndex, inDirection: direction)
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let vc = viewController as? DummyViewController else { return nil }
let nextIndex: Int = vc.index - 1
return viewControllerAtIndex(nextIndex)
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let vc = viewController as? DummyViewController else { return nil }
let nextIndex: Int = vc.index + 1
return viewControllerAtIndex(nextIndex)
}
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
guard let viewController = pageViewController.viewControllers?.first as? DummyViewController else { return }
currentIndex = viewController.index // Update currentIndex, which updates pageControl.currentPage
}
}
Manual reversing to support right-to-left (solution I'm trying to avoid).
Excluded ViewModel and DummyViewController as they're unchanged.
import UIKit
class ViewController : UIViewController, UIPageViewControllerDelegate, UIPageViewControllerDataSource {
let pageViewController = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil)
let pageControl = UIPageControl()
let nextButton = UIButton()
var isRightToLeft: Bool {
traitCollection.layoutDirection == .rightToLeft
}
var currentIndex: Int = 0 {
didSet {
pageControl.currentPage = currentIndex
}
}
var numberOfPages: Int {
return viewModels.count
}
var viewModels: [ViewModel] = [
ViewModel(index: 0, text: "First", color: .red),
ViewModel(index: 1, text: "Second", color: .blue),
ViewModel(index: 2, text: "Third", color: .green),
]
required init?(coder: NSCoder) {
super.init(coder: coder)
pageViewController.dataSource = self
pageViewController.delegate = self
pageControl.currentPage = currentIndex
pageControl.numberOfPages = numberOfPages
pageControl.semanticContentAttribute = .forceLeftToRight
nextButton.setTitle("Next", for: .normal)
nextButton.setTitleColor(.black, for: .normal)
if isRightToLeft { // HERE
viewModels = viewModels.map({ viewModel in
ViewModel(index: (numberOfPages - viewModel.index - 1), text: viewModel.text, color: viewModel.color)
}).reversed()
}
}
override func viewDidLoad() {
addChild(pageViewController)
pageViewController.didMove(toParent: self)
view.addSubview(pageViewController.view)
view.addSubview(pageControl)
view.addSubview(nextButton)
view.subviews.forEach { $0.translatesAutoresizingMaskIntoConstraints = false }
NSLayoutConstraint.activate([
pageViewController.view.topAnchor.constraint(equalTo: view.topAnchor),
pageViewController.view.leftAnchor.constraint(equalTo: view.leftAnchor),
pageViewController.view.rightAnchor.constraint(equalTo: view.rightAnchor),
pageViewController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor),
pageControl.centerXAnchor.constraint(equalTo: view.centerXAnchor),
pageControl.centerYAnchor.constraint(equalTo: nextButton.centerYAnchor),
nextButton.trailingAnchor.constraint(equalTo: view.trailingAnchor),
nextButton.bottomAnchor.constraint(equalTo: view.layoutMarginsGuide.bottomAnchor)
])
nextButton.addTarget(self, action: #selector(nextTapped), for: .primaryActionTriggered)
loadFirstPage()
}
func loadFirstPage() {
let startIndex: Int
if isRightToLeft { // HERE
startIndex = numberOfPages - 1
currentIndex = startIndex
} else {
startIndex = 0
}
guard let start = viewControllerAtIndex(startIndex) else {
return
}
pageViewController.setViewControllers([start], direction: .forward, animated: true, completion: nil)
}
func transitionFrom(index: Int, inDirection direction: UIPageViewController.NavigationDirection) { // UNCHANGED
let nextIndex = direction == .forward ? index + 1 : index - 1
guard let next = viewControllerAtIndex(nextIndex) else {
return
}
pageViewController.setViewControllers([next], direction: direction, animated: true, completion: { finished in
self.pageViewController(self.pageViewController, didFinishAnimating: finished, previousViewControllers: [], transitionCompleted: finished)
})
}
func viewControllerAtIndex(_ index: Int) -> DummyViewController? {
guard index >= 0 && index < numberOfPages else { return nil }
let viewModel = viewModels[index]
return DummyViewController(vm: vm)
}
#objc
func nextTapped(_ sender: UIButton) {
guard (currentIndex < numberOfPages - 1 && !isRightToLeft) || (currentIndex > 0 && isRightToLeft) else { // HERE
return
}
let direction: UIPageViewController.NavigationDirection = isRightToLeft ? .reverse : .forward // HERE
transitionFrom(index: currentIndex, inDirection: direction)
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let vc = viewController as? DummyViewController else {
return nil
}
let nextIndex: Int
if isRightToLeft { // HERE
nextIndex = vc.index + 1
} else {
nextIndex = vc.index - 1
}
return viewControllerAtIndex(nextIndex)
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let vc = viewController as? DummyViewController else {
return nil
}
let nextIndex: Int
if isRightToLeft { // HERE
nextIndex = vc.index - 1
} else {
nextIndex = vc.index + 1
}
return viewControllerAtIndex(nextIndex)
}
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
guard let viewController = pageViewController.viewControllers?.first as? DummyViewController else {
return
}
currentIndex = viewController.index
}
}
Turns out this is much simpler than I realized - setting the direction simply changes the animation direction, it doesn't change what index/view controller is going to be presented.
As such, the solution is simply changing transitionFrom to:
func transitionTo(nextIndex: Int, inDirection direction: UIPageViewController.NavigationDirection) {
// Removed the `next` setting here in favor of getting it passed from `nextTapped`
guard let next = viewControllerAtIndex(nextIndex) else {
return
}
pageViewController.setViewControllers([next], direction: direction, animated: true, completion: { finished in
self.pageViewController(self.pageViewController, didFinishAnimating: finished, previousViewControllers: [], transitionCompleted: finished)
})
}
and changing nextTapped to:
#objc
func nextTapped(_ sender: UIButton) {
guard currentIndex < numberOfPages - 1 else { return }
let direction: UIPageViewController.NavigationDirection = isRightToLeft ? .reverse : .forward // This is key
transitionTo(nextIndex: currentIndex + 1, inDirection: direction)
}
Alternate, less intrusive solution
Alternatively, a less intrusive solution would be to subclass UIPageViewController and simply flip the animation direction if we're in a right-to-left locale.
private extension UIPageViewController.NavigationDirection {
var flipped: Self {
switch self {
case .forward:
return .reverse
case .reverse:
return .forward
#unknown default:
return .reverse
}
}
}
class LocalizedPageViewController: UIPageViewController {
override func setViewControllers(
_ viewControllers: [UIViewController]?,
direction: UIPageViewController.NavigationDirection,
animated: Bool,
completion: ((Bool) -> Void)? = nil
) {
let isRTL = view.effectiveUserInterfaceLayoutDirection == .rightToLeft
let direction = isRTL ? direction.flipped : direction
super.setViewControllers(viewControllers, direction: direction, animated: animated, completion: completion)
}
}
Then I would simply need to change my initialization of the UIPageViewController to LocalizedPageViewController, and no other changes are needed!
I have a UIPageViewController with 4 View Controllers in it. In the second VC, the user enters some data. In the last VC, there is a button that the user clicks to insert these data into DB. I want to pass the data from the second VC to the fourth one. All my search results show segues, but the VCs are not directly connected.
So I thought of using delegates, but it is not working for me.
Code:
UIPageViewController:
import UIKit
class PageViewController: UIPageViewController {
var pageControl = UIPageControl()
fileprivate lazy var pages: [UIViewController] = {
return [
self.getViewController(withIdentifier: "locationStoryBoard"),
self.getViewController(withIdentifier: "notificationStoryboard"),
self.getViewController(withIdentifier: "weightStoryboard"),
self.getViewController(withIdentifier: "bloodTypeStoryboard"),
self.getViewController(withIdentifier: "ageStoryboard"),
self.getViewController(withIdentifier: "genderStoryboard"),
self.getViewController(withIdentifier: "mobileNumberStoryboard"),
self.getViewController(withIdentifier: "preventionStoryboard")
]
}()
fileprivate func getViewController(withIdentifier identifier: String) -> UIViewController
{
return UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: identifier)
}
override func viewDidLoad()
{
super.viewDidLoad()
self.dataSource = self
self.delegate = self
if let firstVC = pages.first
{
setViewControllers([firstVC], direction: .forward, animated: true, completion: nil)
}
configurePageControl()
}
//Page Control (3 dots)
func configurePageControl() {
pageControl = UIPageControl(frame: CGRect(x: 0,y: UIScreen.main.bounds.maxY - 50,width: UIScreen.main.bounds.width,height: 50))
self.pageControl.numberOfPages = pages.count
self.pageControl.currentPage = 0
self.pageControl.alpha = 0.5
self.pageControl.tintColor = UIColor.black
self.pageControl.pageIndicatorTintColor = UIColor.white
self.pageControl.currentPageIndicatorTintColor = #colorLiteral(red: 0.986409843, green: 0.4042935669, blue: 0.4366002679, alpha: 1)
self.view.addSubview(pageControl)
}
}
//MARK: - UIPageViewControllerDataSource methods
extension PageViewController: UIPageViewControllerDataSource
{
func pageViewController(_ pageViewController: UIPageViewController,
viewControllerBefore viewController: UIViewController) -> UIViewController? {
let currentIndex = pages.firstIndex(of: viewController)!
let previousIndex = currentIndex - 1
return (previousIndex == -1) ? nil : pages[previousIndex]
}
func pageViewController(_ pageViewController: UIPageViewController,
viewControllerAfter viewController: UIViewController) -> UIViewController? {
let currentIndex = pages.firstIndex(of: viewController)!
let nextIndex = currentIndex + 1
return (nextIndex == pages.count) ? nil : pages[nextIndex]
}
}
//MARK: - UIPageViewControllerDelegate methods
extension PageViewController: UIPageViewControllerDelegate {
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
let pageContentViewController = pageViewController.viewControllers![0]
self.pageControl.currentPage = pages.firstIndex(of: pageContentViewController)!
}
}
First VC
protocol NameAndMobileDelegates {
func passName(nameToBePassed: String)
}
class MobileNumberViewController: UIViewController, UITextFieldDelegate {
var delegate: NameAndMobileDelegates?
...
func textFieldDidEndEditing(_ textField: UITextField) {
switch textField.tag {
case 1:
name = textField.text!
self.delegate?.passName(nameToBePassed: name)
print(name)
case 2:
mobileNumber = textField.text!
print(mobileNumber)
default: break
}
}
In the 4th VC:
// MARK: - Name and Number Delegate Methods
extension DiseasesViewController: NameAndMobileDelegates {
func passName(nameToBePassed: String) {
print("protocol worked")
}
Thank you in advance
Do
if let fourVC = pages[3] as? FourthVC {
fourVC.delegate = pages.first as! FirstVC
}
then
class FourthVC:UIViewController {
weak var delegate:FirstVC?
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
}
}
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)
}
}