I'm trying to use the change event on a UISwitch to move from the current ViewController to a new one. My UISwitch is registered in my CustomView for the Custom Cell in my UITableView. The action is registered an calls a class in my View Controller as below
import UIKit
public class CustomTableViewCell: UITableViewCell {
#IBOutlet weak var operatedSwitch: UISwitch!
#IBAction func operatedSwitchChange() {
updateValveOps.valveUpdate()
}
When it gets to my class in my ViewController it calls a method in the main class which should move to my new ViewController as below
import UIKit
class updateValveOps {
class func valveUpdate() {
let valveOps = ValveOperationsController()
valveOps.ValveOpsUpdate()
}
}
class ValveOperationsController: UIViewController {
.
.
func ValveOpsUpdate() {
performSegueWithIdentifier("ValveOpsToUpdateSegue", sender: nil)
}
However, this causes a Sigabrt error. I've also tried pushing from the current view to the new View Controller but then for some reason it returns back to the calling View Controller! What am I doing wrong?
Try this in your #IBAction func operatedSwitchChange() { .. }
UIApplication.sharedApplication().keyWindow?.visibleViewController()?.performSegueWithIdentifier("ValveOpsToUpdateSegue", sender: nil)
And add this extension to your project (copy-paste this into new swift file):
public extension UIWindow {
func visibleViewController() -> UIViewController? {
if let rootViewController: UIViewController = self.rootViewController {
return UIWindow.getVisibleViewControllerFrom(rootViewController)
}
return nil
}
class func getVisibleViewControllerFrom(vc:UIViewController?) -> UIViewController? {
if vc == nil {
return nil
}
if let navigationController = vc as? UINavigationController {
return UIWindow.getVisibleViewControllerFrom( navigationController.visibleViewController)
} else if let tabBarController = vc as? UITabBarController {
return UIWindow.getVisibleViewControllerFrom(tabBarController.selectedViewController)
} else {
if let presentedViewController = vc?.presentedViewController {
return UIWindow.getVisibleViewControllerFrom(presentedViewController.presentedViewController)
} else {
return vc
}
}
}
}
I managed to find a way to get this working by using Protocol. First I added a Protocol to my ValveOperationsController and referenced it as below
protocol CustomCellDelegator {
func callSegueFromCell()
}
class ValveOperationsController: UIViewController, CustomCellDelegator {
Then I added a delegate to my cell in cellForRowInIndexPath
cell.delegate = self
Then I added the method called in my Protocol in to my ViewController
func callSegueFromCell() {
performSegueWithIdentifier("ValveOpsToUpdateSegue", sender: nil )
}
Then going to my CustomTableViewCell I added my delegate
var delegate:CustomCellDelegator!
Then in the event called when the Switch changes I added the call to my Protocol
if(self.delegate != nil){ //Just to be safe.
self.delegate.callSegueFromCell()
}
When the event is called when the switch changes it calls the Protocol which passes it to my method and the Segue operates successfully
Related
I need to get a reference (object) of UIViewController in sub class of UIButton. Here I've tried something but failed.
class NavigationBarButton: UIButton {
override func didMoveToSuperview() {
super.didMoveToSuperview()
var viewController: UIViewController? {
var nextResponder: UIResponder? = self
repeat {
nextResponder = nextResponder?.next
if let viewController = nextResponder as? UIViewController {
return viewController
}
} while nextResponder != nil
return nil
}
guard let vcViewController = self.viewController else { print("NavigationBarButton view controller could not found"); return }
print("handle further operations with ViewController of Button")
}
}
class FirstVC: UIViewController {
#IBOutlet weak var myButton: NavigationBarButton?
}
Result:
NavigationBarButton view controller could not found
Is there any other way, without updating UIViewController, I can get view controller reference in sub class of UIButton. Any other UIButton method can help me here, where I can get view controller of button.
Similar SO Que. but not useful for this issue: Get current UIViewController from UIButton's class
This may work for you.
class NavigationBarButton: UIButton {
override func didMoveToWindow() {
super.didMoveToWindow()
var viewController: UIViewController? {
var nextResponder: UIResponder? = self
repeat {
nextResponder = nextResponder?.next
if let viewController = nextResponder as? UIViewController {
return viewController
}
} while nextResponder != nil
return nil
}
guard let vcViewController = self.viewController else { print("NavigationBarButton view controller could not found"); return }
print("handle further operations with ViewController of Button")
}
}
class FirstVC: UIViewController {
#IBOutlet weak var myButton: NavigationBarButton?
}
I am trying to access the instance of a viewController from the UIButton class which is in the framework. Whenever the button gets instantiated from storyboard the init? (coder aDecoder: NSCoder) gets called. But I am not able to understand how to access the `viewController from it.
I have a custom init method init(viewController: UIViewController) in UIButton class, which gives me viewController when button instantiated programmatically.
eg. let button = CustomButton(viewController: self)
But now I want to get this viewController instance when button got init by storyboard i.e. at the time of init? (coder aDecoder: NSCoder) call.
Until now, I am getting viewController object using this,
button.viewController = self
But I think this is not a good practice to get things. I need an alternative way.
Any help would be appreciated. Thanks in advance.
I think you can use this code:
extension UIView {
func findViewController() -> UIViewController? {
if let nextResponder = self.next as? UIViewController {
return nextResponder
} else if let nextResponder = self.next as? UIView {
return nextResponder.findViewController()
} else {
return nil
}
}
}
But in general, you should think about why you need it and is there another way to do it. For example, you can just pass a delegate to the button which will be responsible for this navigation.
This Property is used to getting the ParentViewController(UIViewController) of any UIView.
extension UIResponder {
var viewController:UIViewController? {
if self.next is UIViewController {
return self.next as? UIViewController
} else {
guard self.next != nil else { return nil }
return self.next?.viewController
}
}
}
Finally, with the help of my friend "Pankaj Bawane", I got this working solution.
extension UIView {
var parentViewController: UIViewController? {
var parentResponder: UIResponder? = self
while parentResponder != nil {
parentResponder = parentResponder!.next
if let viewController = parentResponder as? UIViewController {
return viewController
}
}
return nil
}
}
called class:
class LoginViewController: UIViewController {
let chipField: UITextField = {
........
return textField1
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(chipField)
}
}
table to be called:
class MainTableViewController: UITableViewController {
....
}
MainTableViewController call chipfield in LoginViewController
You should really use a delegate in this case, something like
Read more about delegates Here
protocol MainTableViewControllerDelegate {
func getChipFieldValue()
}
class MainTableViewController: UITableViewController {
var delegate: MainTableViewControllerDelegate?
// to get chipFieldValue self.delegate?.getChipFieldValue)
}
In LoginViewController define the function in delegate
class LoginViewController: UIViewController,MainTableViewControllerDelegate {
func getChipFieldValue() ->String {
return chipField.text
}
// later in the code when you present MainTableViewController view pass delegate to self to MainTableViewController object
// something like the MainTableViewControllerObject.delegate = self
}
From your situation all you need is just a property in MainTableViewController
class MainTableViewController: UITableViewController {
var chipFieldCopy:UITextField?
...
}
But I would suggest you rethink if you need the entire UITextField, in most situation you will only need it's text so
class MainTableViewController: UITableViewController {
var chipInfoString:String?
...
}
Is enough.
In your handleLogin() method, you can pass it into MainTableViewController like:
//let navController = UINavigationController(rootViewController: MainTableViewController()) //Replace this line
let mainTableVC = MainTableViewController()
mainTableVC.chipFieldCopy = chipField
let navController = UINavigationController(rootViewController: mainTableVC)
or like I suggest
mainTableVC.chipInfoString = chipField.text
and you can access it in MainTableViewController
I have a viewController with another containerView insider set up to appear temporarily (added programmatically). The containerView is a sort of operation bar, which allows you to change values of the viewController. The protocol called from an IBAction of a button however, does not call the protocol set up inside the viewController class.
Here is the code from both classes:
class viewController: UIViewController, updateListDelegate {
let dataSource = containerView()
override func viewDidLoad() {
super.viewDidLoad()
dataSource.delegate = self
}
func updateList(sender: containerView) {
print("is called") //is not printed
}
}
The code from the containerView:
protocol updateListDelegate {
func updateList(containerView)
}
class containerView: UIViewController {
var delegate: updateListDelegate?
#IBAction func AddSong(_ sender: UIButton) {
self.delegate?.updateList(sender: self)
}
}
If this method is only to be called from one object, then, in my opinion, I would not define a protocol. If multiple objects are to call this method, then I would define a protocol. This is typically how you would call a method backwards, using a basic delegate.
class ViewController: UIViewController {
let container = ContainerView()
override func viewDidLoad() {
super.viewDidLoad()
container.viewControllerDelegate = self
// push to this instance of container at some point
}
func doSomething() {
print("great success")
}
}
class ContainerView: UIViewController {
weak var viewControllerDelegate: ViewController?
#objc func someAction() {
if let viewControllerDelegate = viewControllerDelegate {
viewControllerDelegate.doSomething()
}
}
}
// prints "great success" when someAction() called
One of the most common mistakes people make is not keeping track of instances. For delegates to work, you must be sure you are using the specific instances that you've instantiated and assigned those delegates to.
I am having trouble conforming to the delegate and passing information between two view controllers. I can't seem to understand what I am doing wrong. I want to be able to pass information from one segmented control in CustomCell VC, to receive and display it after a button has been tapped in the CreateEvent VC. Here is the relevant code:
CustomCell VC:
class CustomCell: CreateEventVCDelegate {
var ageDescription : String = String() // setup global variable
func sendageDesciptiongetData(data:String) {
print(ageDescription)
}
#IBAction func ageChanged(sender: UISegmentedControl) {
switch age.selectedSegmentIndex {
case 0:
print("Under 18")
var ageDescription = "under 18"
case 1:
print("Over 18")
var ageDescription = "over 18"
case 2:
print("Strictly over 21")
var ageDescription = "strictly over 21"
default:
print("Other")
}
}
}
CreateEvent VC:
protocol CreateEventVCDelegate {
func sendageDescriptiongetData(ageDescription: String)
}
class CreateEventVC: UIViewController {
var delegate: CreateEventVCDelegate?
#IBAction func saveButtonTapped(sender: UIBarButtonItem) {
var ageDescription = ""
delegate!.sendageDescriptiongetData(ageDescription)
}
}
I think you problem is that you need an instance of CreateEventVC inside CustomCell so you can write:
createEventVC.delegate = self
inside CustomCell.
Anyway I think you don't need a delegation, if you have an instance of CustomCell inside CreateEventVC you can call a public/internal function of CreateEventVC from CreateEventVC:
cell.sendageDesciptiongetData(data)
It seems you have a typo:
You are missing 'r' in CustomCell method
func sendageDesciptiongetData(data:String)
instead of
func sendageDescriptiongetData(data:String)