Back to previous ViewController from class func - ios

I try to close and go back to previous viewController using:
class func closeViewController()
{
presentingViewController?.dismissViewControllerAnimated(true, completion: nil)
}
But this gives me a compilation error.
if i remove the "class" identifyer it works, but i need this function to be class function

Just because you receive a callback from another class doesn't mean that you need your closeViewController method to be a class method. It's likely that it should not be a class method.
presentingViewController is an instance method of UIViewController. There is an implied "self" at the beginning:
self.presentingViewController?.dismissViewControllerAnimated(
true, completion: nil)
However, a class method is performed on the class, not an instance, so self is the class, not an instance.
If you really do need it to be a class method for some reason, you will need to pass in either the current view controller or the presenting view controller as a parameter to the method.
class func closeViewController(theModal: UIViewController)
{
theModal.dismissViewControllerAnimated(
true, completion: nil)
}

try this code :
self.dismissViewControllerAnimated(true, completion: nil)

Related

present a ViewController from a Swift class derived from an NSObject?

This project was written in Objective C and a bridging header and Swift files were added so they can be used at run time. When the app starts, Initializer called in Mock API client is printed in the debugger. Is it possible to present a ViewController from the initializer?
Xcode Error:
Value of type 'MockApiClient' has no member 'present'
//MockApiclient.Swift
import Foundation
class MockApiClient: NSObject
{
override init ()
{
print("Initializer called in Mock API client")
if isLevelOneCompleted == false
{
print("It's false")
let yourVC = ViewController()
self.present(yourVC, animated: true, completion: nil)
} else
{
print("It's true")
}
}
var isLevelOneCompleted = false
#objc func executeRequest()
{
print("The execute request has been called")
isLevelOneCompleted = true
if isLevelOneCompleted {
print("It's true")
} else {
//do this
}
}
}
Update - ViewController.m
// prints "The execute request has been called" from the debugger window
- (void)viewDidLoad {
[super viewDidLoad];
MockApiClient *client = [MockApiClient new];
[client executeRequest];
}
You can't call present(_:animated:completion) because it is a method of UIViewController, not NSObject.
Why not pass a viewController reference to the MockApiClient to present on instead like so. Be sure to check Leaks or Allocations on instruments to avoid the client retaining the controller.
class MockApiClient: NSObject {
var referencedViewController: UIViewController?
override init() {
let presentableViewController = ViewController()
referencedViewController.present(presentableViewController, animated: true, completion: nil)
}
deinit {
referencedViewController = nil
}
}
let apiClient = MockApiClient()
apiClient.referencedViewController = // The view controller you want to present on
Assuming you're using UIKit, you'll have to present the view controller from the nearest available attached view controller. If you know for certain that no other view controllers would currently be presented then you can safely present from the root view controller:
UIApplication.shared.keyWindow?.rootViewController?.present(someViewController, animated: true, completion: nil)
This concept of attached and unattached/detached view controllers is never officially explained but the infamous UIKit warning of presenting view controllers on detached view controllers is real. And the workaround is finding the nearest available attached view controller, which at first (when nothing is currently being presented) is the root view controller (of the window). To then present an additional view controller (while one is currently being presented), you'd have to present from that presented view controller or its nearest parent view controller if it has children (i.e. if you presented a navigation view controller).
If you subclass UIViewController, you can add this functionality into it to make life easier:
class CustomViewController: UIViewController {
var nearestAvailablePresenter: UIViewController {
if appDelegate.rootViewController.presentedViewController == nil {
return appDelegate.rootViewController
} else if let parent = parent {
return parent
} else {
return self
}
}
}
Then when you wish to present, you can simply do it through this computed property:
nearestAvailablePresenter.present(someViewController, animated: true, completion: nil)

swift Dismiss ViewController from a helper / another class

I have a problem when i try to dismiss a view from a function of a helperClass instanced in the viewer class
public func set(playerController: AVPlayerViewController){
playerController?.dismiss(animated: true, completion: nil)
whose view is not in the window hierarchy!
how can I pass correctly the controller so the helper class can dismiss it?
Viewerclass:
helper.add(player: player)
helper.set(playerController: playerController)
You should be able to just do dismiss(animated: true, completion: nil) from the presented view controller, as Apple libraries handle the dismissal both from presenter and the presented view controllers. No need to pass a reference
You also can give a callback to dismiss
something like this:
helper.add(player: player) {
self.dismiss(animated: true, completion: nil)
}
Player:
public func set(playerController: AVPlayerViewController, completion: (Void)->Void){
completion()
}
Try like this from your helper class:-
AppDelegate.sharedInstance().window?.topMostController()?.dismiss(animated: true, completion: nil)
And add this function in your AppDelegate file :-
class func sharedInstance() -> AppDelegate{
return UIApplication.shared.delegate as! AppDelegate
}

swift: How to have same closure to be executed from two separate methods

I have a situation where I have function say
func openViewController(completion:(success:Bool) -> Void)
{
//code here to present some view controller name MYVC
self.presentViewController(myVC, animated: true, completion: {
})
}
From MYVC I get call back with help of delegate in below function in same class as I called above method of openViewController
func handleDismissOfVC(){
self.dismissViewControllerAnimated(true, completion:{
})
}
Now the challenge for me is to call completion block of openViewController of with success flag when I dismiss the view controller with handleDimissOfVC(). How can I achieve this?
You need to create an instance variable to hold the closure. in openViewController, save the closure to that instance variable.
In your handleDismissOfVC function, invoke the block that was saved to the instance variable.
You'd need to provide the completion block to MyVC as a property, like this:
class MyVC {
// Completion callback for when this VC is dismissed.
var complete: ((success: Bool) -> Void)?
func dismiss() {
self.dismissViewControllerAnimated(true, completion:{
if let c = complete {
c(success: true)
}
})
}
}
func openViewController(completion:(success:Bool) -> Void) {
// Pass the completion block to the VC.
myVC.complete = completion
self.presentViewController(myVC, animated: true, completion: {
})
}
I think something like that should do it. Although, this looks like it may be a better fit for the delegate pattern, depending on where the original block is originating from.

Pushing Navigation Controller From Other Class in Swift

I have a Data Manager class that handles some JSON activities. When one action is completed, I want to push to the Navigation controller.
The FirstViewController calls a function on the DataManager Class
DataManager Class
class func extract_json(data:NSData) -> Bool {
//Success
FirstViewController().pushToValueView()
}
FirstViewController Class
func pushToValueView() {
println("Push to Value View")
navigationController?.pushViewController(ValueViewController(), animated: true)
}
In this example, println() is called but no push occurs. If I call pushToValueView() within FirstViewController e.g. self.pushToValueView() (For debug purposes), a push does occur.
Try calling this function on an existing instance of FirstViewController.
I think that in this example you try to push view controller to FirstViewController which is deallocated after exiting the method scope. That is why view controller doesn't appear
Unwrap the optional. Edit: You shouldn't call your vc from a model class btw, thats bad MVC, I'd use an observers rather and get notified when the closure is complete
if let nav = self.navigationController {
println("Push to Value View")
nav.pushViewController(ValueViewController(), animated: true)
}
You can modify your extract_json function so that it accepts a closure to be executed once the data has been extracted. The closure will contain the code to push the new view controller and it will execute in the context of the calling rather than called object:
In DataManager.swift -
func extractJSON(data:NSData, completion:() -> Void) {
//Do whatever
completion()
}
In FirstViewController.swift -
func getDataAndSegue() {
extractJSON(someData, completion:{
navigationController?.pushViewController(ValueViewController(), animated: true)
}
)
}

Retrieve var value from another class

i have those two classes and i want to retrieve a value from one to another:
class SearchUserViewController:UIViewController{
var selectedUser: String!
#IBAction func btn_accept(sender: AnyObject) {
selectedUser = "Norolimba"
self.dismissViewControllerAnimated(true, completion: nil)
}
}
I'm saving the value to "selectedUser" var, then i want to check the value from this class:
class CalendarViewController: UIViewController {
override func viewDidAppear(animated: Bool) {
let vc : SearchUserViewController! = self.storyboard.instantiateViewControllerWithIdentifier("searchView") as SearchUserViewController
println("\(vc.selectedUser)")
if vc.selectedUser == nil {
self.requestData("team")
}else{
self.requestData("user")
}
}
}
But when i println the value "vc.selectedUser" the value is nil. So what can i do here to catch it from the other class and don't get a nil value?
searchView is here:
Hope you can help me.
Thanks
When you use instantiateViewControllerWithIdentifier(), you're not accessing the view controller that was being displayed on the screen, nor are you accessing the controller that has potentially been automatically instantiated by Interface Builder.
What you're doing is instantiating (hence the name) a new instance of that controller. So the instance variable selectedUser of that new instance is going to be nil.
What you should do is probably provide a callback to your SearchUserViewController when you display it, so that it can notify the view that presented it when a user is picked.
Alternatively, you can use the parentViewController property of UIViewController in cases where you (the view controller) are being presented modally to access the view controller that presented you. So in your SearchUserViewController, when it's being dismissed, it can access self.parentViewController, which should be a CalendarViewController, and call a method or set a property.
(But for the modal controller to assume who its parent is, is a bit of a code smell. I recommend using a callback or delegate of some sort.)
Edit: An example of using the completion callback:
class CalendarViewController : UIViewController {
public func displayUserSelectionController() {
let suvc : SearchUserViewController = ... (how are you displaying it?) ...
self.presentViewController(suvc, animated:true, completion: {
// The SUVC is hiding
if let user = suvc.selectedUser {
self.requestData("team")
} else {
self.requestData("user")
}
})
}
...
}

Resources