I'm using a library that manages the swiping between view controllers.
It seems like the only way that I can use a button in one of my view controller to navigate to the other is by calling a specific method from the view controller that manages the swiping feature.
It there any way to call a method in a different view controller?
Thank you
There are many ways to communicate between view controllers. One of the most common patterns for doing so on iOS is the delegate pattern. One view controller holds a reference to the other and the delegate conforms to a protocol. The protocol is a set of methods that the first view controller can call when specific events happen. I would try setting up a protocol and have one of your view controllers be a delegate to the other
If you take a look at the code that is hosted on GitHub you can see that the page controller is exposed from the EZSwipeController class. So given that you subclass EZSwipeController (or maintain a reference somewhere), you can now access the pageViewController property of it and scroll to a given page.
If you subclass:
class MyVC : EZSwipeController{
func buttonTapped(){ /* use self.pageViewController to "page" */ }
}
Weirdly, I have never personally worked with UIPageViewController, and as far as I can tell there is no easy way to scroll to a page in an easy fashion.
I haven't personally tried it (I usually validate my answers before posting), but you should be able to pull it off. If you don't have a reference to the view controller you need to get it (let me know if that is the core issue).
There is a question on SO that seems to enjoy some popularity regarding UIPageViewController paging:
UIPageViewController, how do I correctly jump to a specific page without messing up the order specified by the data source?
And from Apple docs:
I would also encourage you to look at the code inside of the EZSwipeController repo, where you can find a non-exposed method that does the scrolling:
#objc private func clickedLeftButton() {
let currentIndex = stackPageVC.indexOf(currentStackVC)!
datasource?.clickedLeftButtonFromPageIndex?(currentIndex)
let shouldDisableSwipe = datasource?.disableSwipingForLeftButtonAtPageIndex?(currentIndex) ?? false
if shouldDisableSwipe {
return
}
if currentStackVC == stackPageVC.first {
return
}
currentStackVC = stackPageVC[currentIndex - 1]
pageViewController.setViewControllers([currentStackVC], direction: UIPageViewControllerNavigationDirection.Reverse, animated: true, completion: nil)
}
The "key" line is this one:
pageViewController.setViewControllers([currentStackVC], direction: UIPageViewControllerNavigationDirection.Reverse, animated: true, completion: nil)
I think EZSwipeController also exposes the view controllers contained in the pageViewController which is a property called stackVC, which is an array of the view controllers contained in the page view controller.
I assume with this knowledge you should be able to page to a given page, despite seeming a little "hacky" (IMHO the developers should have exposed paging logic from the get-go).
My advice for you after all this is simple:
Try to roll it yourself. This is not a huge undertaking and you maintain full control over what's accessible and how you want to work with it. The EZSwipeController class is quite small, so there is not a whole lot of code you'd have to write for your own solution. You could also go ahead and fork the repo and modify/use it to your liking.
I hope that helps. If you have any further questions or if something is unclear, I'd be ready to help you out.
Related
My situation is, I have a navigation controller(nv) with root view controller(rootVC). And another view controller(firstChildVC) pushed to rootVC. And one more view controller(secondChildVC) pushed to firstChildVC. (In real case, I have more subsequence child view controllers) After API calls and some calculations in secondChildVC, I need to pass some data from secondChildVC back to rootVC and popToRootViewController to show some data.
I don't think delegate and closures are good choice in this case. The only thing I could come up with is using NotificationCenter. Just what to know is there any better way to do this?
Thank you in advance.
You can try this inside the SecondVC then pop to root
if let root = self.navigationController?.viewControllers.first as? RootVC {
root.sendData(data)
}
I think there are three valid ways to do this without overly tightly coupling things:
Notification Center
Closures
Delegate pattern
The right way is not a simple choice. It really depends on the details. You mention making an API call. Assuming this is all managed in a separate object and not coded into your view controller code, I would have the object making the API call post a notification and the view controllers can each listen and do whatever is appropriate.
There are two other ways to do this, one is using Delegates another one is Closures
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
I have been trying to learn how delegation with protocols work. I understood everything, but I can't think of when to use delegation other than when using table views and possibly scroll views.
In general, when is delegation used?
What is Delegation?
First of all, you should know that Delegation Pattern is not exclusive for iOS world:
In software engineering, the delegation pattern is a design pattern in
object-oriented programming that allows object composition to achieve
the same code reuse as inheritance.
But working with delegation in the iOS world is so common, I assume that you can see many of classes that provide a delegation/datasource for giving the ability to provide properties or behaviors for the used instance. It is one of main mechanisms of how objects talk to each other in CocoaTouch.
Alternatives:
However, delegation is not the only way to let objects talk to each other in iOS, you might want to know that there are:
NotificationCenter.
KVO (Key-Value Observing).
Completion handlers/Callbacks (using closures).
Target-Action.
Remark: in case if you are interested in comparing between them, you might want to check the following articles:
Communication Patterns.
When to Use Delegation, Notification, or Observation in iOS.
Delegates vs Observers.
When to use Delegation?
So, the question is: "So why should I use delegation instead of those options?"
I will try to make it simple; I would suggest the use of delegation when you have one to one relationship between two objects. Just to make it clearer, the goal of talking a little bit about the NotificationCenter is to try to make sense when delegations are used:
NotificationCenter represents one to many relationship; Simply, it works as: posting (notifying) a notification on a specific event and observing (get notified about) this notification -- it could be observed anywhere else; Logically, that's what one to many relationship means. It is a representation of the Observer Pattern.
How to Apply Delegation?
For the purpose of simplifying, I would mention it as steps:
Knowing the requirements: Each delegate has its own rules, listed in the delegate protocol which is a set of method signatures that you should implement for conforming this delegation.
Conforming for the delegation: it is simply letting your class to be a delegate, by marking it. For instance: class ViewController: UIViewController, UITableViewDelegate {}.
Connecting the delegate object: Marking your class to be a delegate is not enough, you need to make sure that the object you want to be confirmed by your class to give the required job to your class.
Implementing the requirements: Finally, your class have to implement all required methods listed in the delegate protocol.
For Example
Does it sounds a little confusing? What about a real-world example?
Consider the following scenario:
Imagine that you are building an application related to playing audios. Some of the viewControllers should have a view of an audio player. In the simplest case, we assume that it should have a play/pause button and another button for, let's say, showing a playlist somehow, regardless of how it may look like.
So far so good, the audio player view has its separated UIView class and .xib file; it should be added as a subview in any desired viewController.
Now, how can you add functionality to both of the buttons for each viewController? You might think: "Simply, I will add an IBAction in the view class and that's it", at first look, it might sound ok, but after re-thinking a little bit, you will realize that it will not be applicable if you are trying to handle the event of tapping the button at the controller layer; To make it clear, what if each viewController implemented different functionality when tapping the buttons in the audio player view? For example: tapping the playlist in "A" viewController will display a tableView, but tapping it in the "B" viewController will display a picker.
Well, let's apply Delegation to this issue:
The "#" comments represents the steps of "How to Apply Delegation?" section.
Audio Player View:
// # 1: here is the protocol for creating the delegation
protocol AudioPlayerDelegate: class {
func playPauseDidTap()
func playlistDidTap()
}
class AudioPlayerView: UIView {
//MARK:- IBOutlets
#IBOutlet weak private var btnPlayPause: UIButton!
#IBOutlet weak private var btnPlaylist: UIButton!
// MARK:- Delegate
weak var delegate: AudioPlayerDelegate?
// IBActions
#IBAction private func playPauseTapped(_ sender: AnyObject) {
delegate?.playPauseDidTap()
}
#IBAction private func playlistTapped(_ sender: AnyObject) {
delegate?.playlistDidTap()
}
}
View Controller:
class ViewController: UIViewController {
var audioPlayer: AudioPlayerView?
// MARK:- Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
audioPlayer = AudioPlayerView()
// # 3: the "AudioPlayerView" instance delegate will implemented by my class "ViewController"
audioPlayer?.delegate = self
}
}
// # 2: "ViewController" will implement "AudioPlayerDelegate":
extension ViewController: AudioPlayerDelegate {
// # 4: "ViewController" implements "AudioPlayerDelegate" requirments:
func playPauseDidTap() {
print("play/pause tapped!!")
}
func playlistDidTap() {
// note that is should do a different behavior in each viewController...
print("list tapped!!")
}
}
Quick Tip:
As one of the most popular examples of using delegation is Passing Data Back between View Controllers.
Delegation is used when you want to pass some information or state of object A to another object B. Usually object B is the object that created object A.
I will list some situations where you would use delegation.
Yes you're right. table views and scroll views use delegates because they want to tell whoever is interested (usuall your view controller) that "someone selected a row!" or "someone scrolled the scroll view!". Not only do scroll views and table views use delegates, UITextField and UIDatePicker and a lot of other views use delegates too!
View Controllers also have delegates. For example, UIImagePickerController. The reason why is roughly the same as above - because the UIImagePickerController wants to tell you messages like "an image has been selected!". Another example would be UIPopoverControllerDelegate. This delegate tells you things like "the popover has been dismissed!"
Other classes that use delegates include CLLocationManager. This delegate tells you things like "the user's location has been detected" or "failed to detect the user's location".
You can use delegation in your code when a certain view controller of yours wants to send messages to other view controllers. If it is a settings view controller, it might send messages like "the font size setting has been changed!" and the view controller that cares about the font size setting changing will know and change the font size of a label or something.
Delegate Method to Selectionimages
Create baseClass And Insert the following code
Create Another class then insert code
Delegation in IOS world and mostly in MVC (Model View Controller)
is a way for the View to talk to the Controller and it's called "blind communication"
and delegation means to give the " leading stick " to another object ( doesn't really care who is taking over but usually the Controller) to control over components that the view can not control on it's own (remember it's only a view) or doesn't own
to make it more simple ....
the controller can talk to a view but the view can not talk to the controller without Delegation
can not find an explanation of how to develop an app that is developed with 3 ViewControllers within a PageView. Somebody know any tutorial?
Is this what you're looking for?
http://shrikar.com/blog/2015/01/17/ios-swift-tutorial-uipageviewcontroller-as-user-onboarding-tool/
I created a Swift tutorial on how to use UIPageViewController with three View Controllers.
It includes two sample projects -- a starter project on which to walk through the tutorial, and a final project to check your answer.
http://www.thorntech.com/2015/08/need-to-create-an-onboarding-flow-for-your-mobile-app-heres-how-to-do-it-using-uipageviewcontroller-in-swift/
But here's the gist of using UIPageViewController:
In viewDidLoad, use setViewControllers and pass in a single View Controller instantiated via Storyboard ID. It takes an array, but you will always pass in just one VC
You can't wire the DataSource using the Storyboard, so you have to do dataSource = self
Now that you're the DataSource, you need to implement viewControllerBeforeViewController and viewControllerAfterViewController.
For viewControllerAfterViewController, figure out which is the current page like so: if viewController.isKindOfClass(PageOneClassName) { }
Once you know the current page, return whatever comes next by instantiating a VC from the Storyboard. If it's the last page, just return nil
For viewControllerBeforeViewController, just return the previous page. Again, return nil if you're at the beginning.
I'm trying to create a reusable component to display some photo collection.
The basic flow is the following :
First view : View. It contains my so called library, designed programmatically and loaded from storyboard by assigning a custom class
I take a photo in a modal view, openend from the 'take picture' button
Once the photo is saved on disk, I ask PhotoLib to create a new PhotoCell from the photo path
I would like my PhotoCell to be touch enabled so when I tap it, it opens the second view in a modal way, but from what I read I cannot do this from my PhotoCell or the UIImageView inside (not a controller).
So how can I do ? View is embedded in a NavigationController, even if not shown in the screenshots below.
Thank you !
If you create Photocell in photolib, then photolib should implementing delegate methods from photocell. But photolib itself is not rootviewcontroller, so it should declare delegate methods itself, and the containing view should implement it.
Basically you pass Photocell from itself to Photolib (which implements delegate method
-(void) openPhotoCell:(Photocell*)cell
{
[self.delegate openPhotocell:(Photocell*)cell];
}
, then it passes it to View, which in its turn opens it.
It may seem like pulling a tooth from an ear, but actually it's quite working and if you write good self-explanatory code, it's not a problem. I'm currently working on some big project with tens views and controllers and it works pretty good and nobody has problem with that.
If you have more layers, then maybe you should look into NSNotification.
Hope it helped, I'd be glad to explain more.
UPD:
Links:
about delegates in cocoa fundamentals guide
delegation pattern in wikipedia
Short version
If I'm writing my own custom split view controller, what do I need to do to make the child view controllers work as expected?
For instance: to send viewWillAppear: and so on.
Long version
Background
A while back I answered the following question:
Switch between UIViewControllers using UISegmentedControl
With the following answer:
The right way to do it is to have the controller handling the UISegmentedControl add the views of the controllers as subviews.
[self.view addSubview:controller.view];
It's your responsibility to send viewWillAppear: and so on.
However, tc. pointed out that this is not as trivial as it sounds:
No. View controllers are not meant to be used like that - controller will miss out on a lot of the UIViewController magic that's taken for granted (namely -view{Will,Did}{Appear,Disappear}: and -shouldRotateToViewOritentation:).
By "magic", I'm referring to everything UIKit does behind the scenes. You also forgot -parentViewController (which is important for things like modal view controllers). Additionally, somewhere in the depths of UIKit, it automatically calls -viewSomethingSomething: for you, so you might get -viewDidDisappear: twice! (I can't remember the exact details, but there's another user reporting that all you need to do is call -viewWillAppear: and the other three methods happen automatically.) The key issue is that Apple doesn't document the "magic" or how it changes between OS updates.
Since then I've been thinking that one should compile a guide for what needs to be present in the parent view controller in order for the child view controllers to work as expected. Apple's documentation doesn't cover this and Google didn't help much either, so now I'm hoping to find this knowledge within the stackoverflow community.
I've written several controllers like this and they all seem to work, but I can't help but wonder if I'm missing out on important view controller magic.
I think the best solution is "don't do that". Instead of hoping that you can duplicate all of UIViewController's behavior without being rejected for using private API calls why not create non-UIViewController controller objects to manage your subviews? A "controller" is not necessarily a UIViewController.
At a minimum you would need to override or mix in replacement getters for parentViewController, splitViewController, navigationController, tabBarController, and interfaceOrientation (and probably also modalViewController). For each property you would need to make sure that any private setter called by UIKit still works as expected and any changes to those values made by modifying UIViewController ivars directly are also correctly reflected in your implementations.
You're also going to need to figure out how UIKit determines which UIViewController is currently active and should receive view controller lifecycle methods because you need to make sure that these are sent to your container view controller and not only to one of it's children.
You will also have to hope that you haven't just constructed a situation which isn't supported by any of Apple's view controller classes. For example will any of them break if they have a parentViewController but their navigationController, tabBarController, and splitViewController are all nil?
Finally you'll need to keep up with any changes to these private implementation details with every iOS release.