My Scenario, I am trying to call a ViewController class file particular function from another one class file. Here, I am getting below warning and ViewController not presenting.
My Code Below ViewControllerA
func previewview(){ // Inside ViewControllerA
DispatchQueue.main.async {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let fileViewController = storyboard.instantiateViewController(withIdentifier: "fileviewcontroller")
let navController = UINavigationController(rootViewController: fileViewController)
self.present(navController, animated: true, completion: nil)
} }
Below code In Another Class File
import UIKit
class FileController {
//MARK:- Call a file preview
ViewControllerA().self.previewview()
}
Warning: Attempt to present on
whose view is not in the window
hierarchy!
Try this
Viewcontroller().FuncName()
Example:
LoginViewController().checkLoginValidation()
Use swift's Delegate Protocol to call a function in a class from different class.
Here is a link for understanding how delegate protocol works.
https://medium.com/#nimjea/delegation-pattern-in-swift-4-2-f6aca61f4bf5
Here is how you can proceed. This is just an example. You can take the idea and implement in you code as per your requirement.
Instead of creating a separate ViewController, create a ViewModel that handles the implementation of saving the file, i.e.
class SaveOptionsViewModel {
func save(file: String, handler: (()->())?) { //add parameters to save a file as per requirement
//save the file here...
handler?()
}
}
Now, in the controller that contains multiple save options, create a property of type SaveOptionsViewModel.
And present PreviewVC from SaveOptionsVC in the handler once the file is saved using the SaveOptionsViewModel after tapping the saveButton.
class SaveOptionsVC: UIViewController {
let viewModel = SaveOptionsViewModel()
#IBAction func onTapSaveButton(_ sender: UIButton) {
self.viewModel.save(file: "") {
if let previewVC = self.storyboard?.instantiateViewController(withIdentifier: "PreviewVC") {
self.present(previewVC, animated: true, completion: nil)
}
}
}
}
Add the custom implementation of PreviewVC as per your requirement.
class PreviewVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
//add the code...
}
Related
I have a mainTabBarController and inside it a mainViewController
I have the mainTabBarController's instance in the mainViewController
The problem is that when I add a notification call for the hello() function in the mainTabBarC, then it gets called twice
mainTabBarController:
class MainTabBarController : UITabBarController {
// Main Code
override func viewDidLoad() {
print("viewDidLoad")
NotificationCenter.default.addObserver(self, selector: #selector(hello), name: "sayHello", object: nil)
}
#objc func hello(){
print("Hello")
}
}
mainViewController:
class MainViewController: UITableViewController {
// Classes
let mainTabBarController = MainTabBarController()
}
And in AppDelegate I wanna call the hello function whenever app becomes active
func applicationDidBecomeActive(_ application: UIApplication) {
NotificationCenter.default.post(name: "sayHello", object: nil)
}
Now the problem is, that I have the mainTabBarC, and inside it I have the mainViewController which contains the mainTabBarC too..
And the hello() function will be called 2x times
How can I call a MainTabBarController function from MainViewController without creating a whole new instance?
Your MainViewController already holds a reference to MainTabBarController:
You could use it in two different ways:
// Option 1
if let tabBarController = tabBarController {
// do something with your tabBarController
}
// Option 2
guard let tabBarController = tabBarController else { return }
// do something with your tabBarController
I'm not sure if I'm doing it right, but the correct code in MainViewController might be:
var mainTabBarController: MainTabBarController!
override func viewDidLoad() {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
mainTabBarController = storyboard.instantiateViewController(withIdentifier: "MainTabBarController") as! MainTabBarController
}
I'm learning Swift/Xcode and trying to create an app with three pages that you can swipe back and forth using a Page View Controller. The issues I'm having lie in the UIViewController subclass, more specifically the viewDidLoad() function. I'm getting errors like "value of type 'NameOfMyClass' has no member datasource/delegate" and "use of unresolved identifier "setViewControllers". I've followed many tutorials and checked other posts but no one seems to be having these issues. When I attempted to run this I would get a black screen, and now I get a terminated due to signal 15 error.
Here's the relevant code where the errors are popping up:
import UIKit
class RootPageViewController: UIViewController,
UIPageViewControllerDataSource, UIPageViewControllerDelegate {
lazy var viewControllerList:[UIViewController] = {
return [self.VCInstance(name: "MissionOne"),
self.VCInstance(name: "MissionTwo"),
self.VCInstance(name: "MissionThree")]
}()
private func VCInstance(name: String) -> UIViewController {
return UIStoryboard(name: "Main", bundle:
nil).instantiateViewController(withIdentifier: name)
}
override func viewDidLoad() {
super.viewDidLoad()
self.dataSource = self //...has no member 'dataSource' error
self.delegate = self //...has no member 'delegate' error
if let MissionOne = viewControllerList.first {
setViewControllers([MissionOne], direction: .forward, animated:
true, completion: nil)
//use of unresolved indentifier 'setViewControllers' error
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
The other issue I'm having, and it may be related, is that the Page View Controller is not accepting/recognizing my class file to set as the custom class in the story board.
Thanks in advance for any insight or suggestions.
your RootPageViewController is subclass of UIViewController, not a UIPageViewController
I looks to me that you are mixing two different controllers here.
What you want to do is add 3 RootPageViewController to a UIPageViewController, yet here you are trying to add 3 RootPageViewControllers to a RootPageViewController!
One way to fix it is to have a RootPageViewController and a separate UIPageViewController which will contain all RootPageViewControllers like so:
// This is a UIPageViewController containing all RootPages
class PagesViewController: UIPageViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate {
lazy var viewControllerList:[UIViewController] = {
return [RootPageViewController.VCInstance(name: "MissionOne"),
RootPageViewController.VCInstance(name: "MissionTwo"),
RootPageViewController.VCInstance(name: "MissionThree")]
}()
override func viewDidLoad() {
super.viewDidLoad()
self.dataSource = self
self.delegate = self
if let MissionOne = viewControllerList.first {
setViewControllers([MissionOne], direction: .forward, animated:
true, completion: nil)
}
}
}
And the RootPageViewController which will be added in the UIPageViewController above:
// This is a UIViewController representing a single page
class RootPageViewController: UIViewController {
static func VCInstance(name: String) -> UIViewController {
return UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: name)
}
// Do what you need a page to do...
}
With such a construction you will have all your errors solved and you will also be able to use PagesViewController as custom class in your Storyboard!
I am really struggling on an issue that I think is rather interesting and quite difficult. My application lets the user create annotation locations within a Mapview. They also have the option to edit and delete these locations in another modal view controller.
The issue I am facing is that when the user presses delete, which removes the location from firebase, the annotation is still displayed upon the map. I cannot reload my annotation data within the view did appear as this does not suit my application. I cant have my annotations being reloaded every time I bring up the Mapview.
I need to figure out a way to implement an annotation reload when the delete button is pressed. However, as this happens within my delete view controller (which does not contain the mapView) I cannot use the reload function. Is there a way to connect view controllers so that I can apply the reload function when delete is pressed?
Updated Code **
This is my map view controller:
class ViewController: UIViewController, SideBarDelegate, MGLMapViewDelegate, DeleteVCDelegate {
let EditSaveSpotController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "EditVC") as! EditSaveSpotViewController
override func viewDidLoad() {
super.viewDidLoad()
EditSaveSpotController.delegate = self
}
func wholeRefresh() {
let uid = FIRAuth.auth()!.currentUser!.uid
let userLocationsRef = FIRDatabase.database().reference(withPath: "users/\(uid)/personalLocations")
userLocationsRef.observe(.value, with: { snapshot in
for item in snapshot.children {
guard let snapshot = item as? FIRDataSnapshot else { continue }
let newSkatepark = Skatepark(snapshot: snapshot)
self.skateparks.append(newSkatepark)
self.addAnnotation(park: newSkatepark)
}
})
if let annotations = mapView.annotations {
mapView.removeAnnotations(annotations)
}
for item in skateparks {
self.addAnnotation(park: item)
}
}
This is my delete view controller:
import UIKit
import Firebase
protocol DeleteVCDelegate {
func wholeRefresh()
}
class EditSaveSpotViewController: UIViewController {
var delegate: DeleteVCDelegate?
#IBAction func deleteSkateSpot(_ sender: Any) {
ref = FIRDatabase.database().reference(withPath: "users").child(Api.User.CURRENT_USER!.uid).child("personalLocations/\(parkId!)")
ref.observe(.value, with: { (snapshot) in
self.ref.setValue(nil)
self.dismiss(animated: true, completion: nil)
self.delegate?.wholeRefresh()
// self.delegate?.mainRefresh()
print("CheckWorking")
})
}
}
This is very high level and I did not have a chance to verify but it should be enough to get you going:
Modal Delete View
protocol DeleteVCDelegate {
func mainRefresh()
}
class DeleteVC: UIViewController {
var delegate: DeleteVCDelegate?
//your delete code
#IBAction func deleteSkateSpot(_ sender: Any) {
ref = FIRDatabase.database().reference(withPath: "users").child(Api.User.CURRENT_USER!.uid).child("personalLocations/\(parkId!)")
ref.observe(.value, with: { (snapshot) in
self.ref.setValue(nil)
//call to delegate
self.delegate?.mainRefresh()
})
}
}
MapView Class (implement DeleteVCDelegate)
class mapVC: MKMapViewDelegate, DeleteVCDelegate{
//when you present your DeleteVC set its delegate to the map view
let vc=(self.storyboard?.instantiateViewController(withIdentifier: "deleteVC"))! as! DeleteVC
//set the delegate
vc.delegate=self
//present deleteVC
self.present(vc, animated: true, completion:nil)
//implement delegate method of DeleteVC
func mainRefresh(){
//dismiss modal
self.dismiss(animated: true) {
//update view
self.loadLocations()
self.annotationRefresh()
}
}
}
So i have a UIButton with its own custom class (open class circlemenu: UIButton), the button is placed on a view controller but the code for the button is in a seperate swift file.
as the custom class produces results i would like it to change from the viewcontroller where the button is physically place to another viewcontroller.
at them moment I'm just getting an error as i can't do like i normally would from a VC item as the button has no self??:
class circlemenu {
var parentViewController:UIViewController!
func presentNewViewController() {
let newViewController = parentViewController.storyboard?.instantiateViewControllerWithIdentifier("YOUR STORYBOARD ID") as UIViewController
parentViewController.presentViewController(newViewController, animated: true, completion: nil)
}
}
i have also tried setting up a relationship with the unbutton and the viewcontroller with the following in my custom class
var parentViewController:UIViewController!
func presentNewViewController() {
let newViewController = parentViewController.storyboard?.instantiateViewControllerWithIdentifier("storyboardID") as UIViewController
parentViewController.presentViewController(newViewController, animated: true, completion: nil)
}
and this in the viewcontroller class
func myCustomClass() {
var customButton = circlemenu()
customButton.parentViewController = self
}
but it just keeps throwing me errors.
I have 3 UIViewControllers say:
v1ViewController
v2ViewController
v3ViewController.
I have pushed controllers as v2 on v1 and v3 on v2.
Now I want to bring some value back to v1ViewController from v3ViewController using delegates.
On v3Viewcontroller I wrote it:
for vc in self.navigationController!.viewControllers{
if vc is v1ViewController{
delegate?.returnFilteredImage(imageView.image!)
self.navigationController?.popToViewController(vc, animated: true)
}
}
How can I use delegates because in v1ViewController I haven't create object of v3Viewcontroller; consequently I cannot connect delegate to self.
So how can i do that.
1. Using delgates
create a protocol and implement in your firstviewcontroller
protocol My {
func returnFilteredImage(image: UIImage)
}
class FirstViewController: UIViewcontroller, My {
...
func returnFilteredImage(image: UIImage) {
}
}
and in your thirdViewController create a property and assign FirstViewController delegate to this.
class ThirdViewController: UIViewController {
var delegate: My?
...
override func viewDidLoad() {
super.viewDidLoad()
for vc in self.navigationController!.viewControllers{
if vc is FirstViewController {
let vc1 = vc as! FirstViewController
self.delegate = vc1
self.delegate?.returnFilteredImage(imageView.image!)
self.navigationController?.popToViewController(vc, animated: true)
}
}
}
}
2. Using local notifications
check here
You can do this using local notifications
class FirstViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
//add observer
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(self.didgetImage(_:)), name: "receiveImageNotification", object: nil)
}
func didgetImage(notification: NSNotification) {
if let image = notification.userInfo?["image"] as? UIImage {
// do something with your image
}
}
}
and from third view controller, first notification
let imageDataDict:[String: UIImage] = ["image": image]
NSNotificationCenter.defaultCenter().postNotificationName("receiveImageNotification", object: self, userInfo: imageDataDict)
Hope this helps :)