Conditional cast from UIViewController always succeeds | Swift/Xcode - ios

What am I doing wrong here? This still functions fine, but I would like to get rid of the yellow warning if I can help it. The warning is on the "if" statement. If I remove the "?" on "as", then the code won't even run... it requires it yet complains about it.
The warning:
Conditional cast from 'UIViewController' to 'UIViewController' always succeeds
The code:
class FadeInPushSegue: UIStoryboardSegue {
var animated: Bool = true
override func perform() {
if let sourceViewController = self.source as? UIViewController, let destinationViewController = self.destination as? UIViewController {
let transition: CATransition = CATransition()
transition.type = CATransitionType.fade; sourceViewController.view.window?.layer.add(transition, forKey: "kCATransition")
sourceViewController.navigationController?.pushViewController(destinationViewController, animated: false)
}
}
}

You don't need to cast it to UIViewController because properties source and destinations is UIViewController already
open var source: UIViewController { get }
open var destination: UIViewController { get }
You seeing this warning because you cast from not Optional UIViewController to optional UIViewController.
When you remove as? your code isn't running because you trying to unwrap not optional property.
Initializer for conditional binding must have Optional type, not 'UIViewController'
You should remove if do something like that:
final class FadeInPushSegue: UIStoryboardSegue {
var animated: Bool = true
override func perform() {
let sourceViewController = self.source
let destinationViewController = self.destination
let transition: CATransition = CATransition()
transition.type = CATransitionType.fade; sourceViewController.view.window?.layer.add(transition, forKey: "kCATransition")
sourceViewController.navigationController?.pushViewController(destinationViewController, animated: false)
}

Related

How to get the current visible viewController from AppDelegate

So, I'm using the method bellow from UIApplication extension to get the top view controller:
class func topViewController(controller: UIViewController? = UIApplication.shared.keyWindow?.rootViewController) -> UIViewController? {
if let navigationController = controller as? UINavigationController {
return topViewController(controller: navigationController.visibleViewController)
}
if let tabController = controller as? UITabBarController {
if let selected = tabController.selectedViewController {
return topViewController(controller: selected)
}
}
if let presented = controller?.presentedViewController {
return topViewController(controller: presented)
}
return controller
}
But the problem is: It always returns UIViewController. But I need to check if it is MyViewController for example. How do I achieve that?
Do conditional casting on the return value to safely check its type.
if let currentVC = UIApplication.topViewController() as? MyViewController {
//the type of currentVC is MyViewController inside the if statement, use it as you want to
}
Your whole function implementation is flawed, if it actually worked, it would lead to infinite recursion. Once you find out the type of your current top view controller in your if statements, you are calling the same function again with the current root controller as its input value. Your function only ever exists, if it reaches either a call from a view controller, whose class is none of the ones specified in your optional bindings.
Moreover, your whole implementation doesn't do anything at the moment. You find out the type of your root view controller, but than you upcast it by returning a value of type UIViewController.
You can do a conditional check with an if-let statement like this:
if let presented = controller?.presentedViewController as? MyViewController {
// it is a MyViewController
}
You can also just directly check if the UIViewController is that type of class like this:
if controller?.presentedViewController is MyViewController {
// it is a MyViewController
}
Try this:
if let presented = controller?.presentedViewController as? MyViewController {
...
You can check it in following ways
class func topViewController(controller: UIViewController? = UIApplication.shared.keyWindow?.rootViewController) -> UIViewController? {
if let navigationController = controller as? UINavigationController {
return topViewController(controller: navigationController.visibleViewController)
}
else if let tabController = controller as? UITabBarController {
if let selected = tabController.selectedViewController {
return topViewController(controller: selected)
}
}
else if let presented = controller?.presentedViewController {
return topViewController(controller: presented)
}
return controller
}
// Answer
if let topVC = AppDelegate.topViewController() as? MyViewController {
// Here your topVC is MyViewController
}
// or
if let topVC = AppDelegate.topViewController() {
if topVC is MyViewController {
// Here your topVC is MyViewController
}
}
To use the UIViewController as MyViewController:
if let myViewController = UIApplication.topViewController() as? MyViewController { ... }
or if you just want to check that the UIViewController is of type MyViewController:
if UIApplication.topViewController() is MyViewController { ... }

type casting when creating an instance from a parent class

I would like to ask something about type-casting in Swift.
There are 2 classes.
RootViewController
MyViewController
and the class hierarchy is like below:
class RootViewController: UIViewController {
}
class MyViewController: RootViewController {
}
and, I want to simply call instance function to create an instance from xib file.
so I implemented below function in RootViewController.
Objective-C
+ (instancetype)instance {
return [[[self class] alloc] initWithNibName:NSStringFromClass([self class]) bundle:nil];
}
Swift
public class func instance<T:RootViewController>() -> T {
let type = self as UIViewController.Type
let name = NSStringFromClass(type).components(separatedBy: ".").last!
let instance = type.init(nibName: name, bundle: nil)
return instance as! T
}
and, usage is like below.
Objective-C
MyViewController *vc = [MyViewController instance];
Swift
let vc = MyViewController.instance() as! MyViewController
Question:
Do I have to always cast the type of instance using as! MyViewController in Swift?
Or can anybody advise me a better approach in Swift?
Any help would be appreciated!
You can Also use like this way
let vc:MyViewController = MyViewController.instance()
class RootViewController: UIViewController {
public class func instance() -> Self {
func inner<T: RootViewController>(type: T.Type) -> T {
let name = NSStringFromClass(type).components(separatedBy: ".").last!
let type1 = type as UIViewController.Type
let instance = type1.init(nibName: name, bundle: nil)
return instance as! T
}
return inner(type: self)
}
}
I would suggest creating an extension method:
extension UIViewController {
public class func instance() -> Self {
func inner<T: UIViewController>(type: T.Type) -> T {
let name = NSStringFromClass(type).components(separatedBy: ".").last!
return T(nibName: name, bundle: nil)
}
return inner(type: self)
}
}
Okay you can instantiate in these three ways:
Swift inference to Type:
let myVC = RootViewController.instance() //Swift will automatically infer the type
Explicitly telling the Type:
let myVC: RootViewController = RootViewController.instance()
Casting to your Type:
let myVC = RootViewController.instance() as! RootViewController
All these three are valid.

Global Popover Function in Swift 1.2 on iOS 8

I have this function that I use all over my app, and it would be nice to create a global function:
class CustomReportVC: UIViewController, UIAdaptivePresentationControllerDelegate, UIPopoverPresentationControllerDelegate {
func showPicker(pickerValues:[String], field:UITextField) -> AnyPickerVC{
//Set up modal
let storyboard = UIStoryboard(name: "Popovers", bundle: nil)
let modal = storyboard.instantiateViewControllerWithIdentifier("AnyPickerModal") as! AnyPickerVC
modal.modalPresentationStyle = UIModalPresentationStyle.Popover
let pc = modal.popoverPresentationController
pc?.permittedArrowDirections = .Down
pc?.sourceView = field
pc?.sourceRect = field.bounds
modal.preferredContentSize = CGSizeMake(300,180)
pc?.delegate = self
//Pass in data
modal.data = pickerValues
//Set the value from within the picker controller
modal.passDataToParent = { (value) in
field.text = value
}
return modal
}
//Required for the popover
func adaptivePresentationStyleForPresentationController(controller: UIPresentationController) -> UIModalPresentationStyle {
return .None
}
}
The issue I'm running into comes with pc?.delegate = self. Since CustomReportVC conforms to UIPopoverPresentationControllerDelegate, this works fine.
But once I attempt to create this as a global function outside a class that conforms to this protocol, I get an error:
func globalShowPicker(pickerValues:[String], field:UITextField, controller:UIViewController) -> AnyPickerVC{
//...
pc?.delegate = controller //<-- ( ! ) Type UIViewController does not conform to UIPopoverPresentationControllerDelegate
}
Whether I make controller a UIViewController or AnyObject, it doesn't conform. Is there a way to pass in the protocol conformity to the global function somehow?
Any idea how I can pull this off? Thanks. :)
Make your global function generic to specify that it only works for certain kinds of UIViewControllers. In this example, T can take the value of any UIViewController type which also conforms to the other protocols listed.
func globalShowPicker< T: UIViewController where
T: UIPopoverPresentationControllerDelegate,
T: UIAdaptivePresentationControllerDelegate >
(pickerValues:[String], field:UITextField, controller: T) -> AnyPickerVC
{
//...
pc?.delegate = controller
return blah
}
It does get kinda long, and I haven't figured out the best way to indent all the constraints. But it works.
Try adding a making a new class that inherits from both of them. Like this.
class PopoverController: UIViewController, UIPopoverPresentationControllerDelegate {
}
Then, switch the function to look like this.
func globalShowPicker(pickerValues:[String], field:UITextField, controller: PopoverController) -> AnyPickerVC{
//...
pc?.delegate = controller
}
You can pass the delegate as a parameter in the function like this:
class CustomReportVC: UIViewController, UIAdaptivePresentationControllerDelegate, UIPopoverPresentationControllerDelegate {
class func showPicker(pickerValues:[String], field:UITextField, delegate: UIPopoverPresentationControllerDelegate) -> UIViewController {
//Set up modal
let storyboard = UIStoryboard(name: "Popovers", bundle: nil)
let modal = storyboard.instantiateViewControllerWithIdentifier("AnyPickerModal") as! UIViewController
modal.modalPresentationStyle = UIModalPresentationStyle.Popover
let pc = modal.popoverPresentationController
pc?.permittedArrowDirections = .Down
pc?.sourceView = field
pc?.sourceRect = field.bounds
modal.preferredContentSize = CGSizeMake(300,180)
pc?.delegate = delegate
//Pass in data
modal.data = pickerValues
//Set the value from within the picker controller
modal.passDataToParent = { (value) in
field.text = value
}
return modal
}
//Required for the popover
func adaptivePresentationStyleForPresentationController(controller: UIPresentationController) -> UIModalPresentationStyle {
return .None
}
}
You have at the end to have an instance of a view controller that does conform to the protocol, but that way you will have the function global just like you want and pass self pointer in the view controller that does conform to the protocol "UIPopoverPresentationControllerDelegate":
CustomReportVC.showPicker(pickerValues:....., delegate: self)
Something like this?
self.presentViewController(pc, animated: true, completion: nil)
Btw, if you're doing Universal you can not present iPad UIActivityViewController like iPhone. You need to present it in a popover as per the design guidelines suggested by Apple.
or as an example
#IBAction func shareButtonClicked(sender: UIButton)
{
let textToShare = "Text"
if let myWebsite = NSURL(string: "http://www.example.com/")
{
let objectsToShare = [textToShare, myWebsite]
let activityVC = UIActivityViewController(activityItems: objectsToShare, applicationActivities: nil)
var nav = UINavigationController(rootViewController: activityVC)
nav.modalPresentationStyle = UIModalPresentationStyle.Popover
var popover = nav.popoverPresentationController as UIPopoverPresentationController!
activityVC.preferredContentSize = CGSizeMake(500,600)
popover.sourceView = self.view
popover.sourceRect = CGRectMake(100,100,0,0)
self.presentViewController(nav, animated: true, completion: nil)
}
}
class MyViewController : UIViewController, UIPopoverPresentationControllerDelegate {
//code here
}

Swift - Proper usage of AnyObject

I am new to swift. I am using storyboard and according to logged in information, I am changing the UIViewController to load. Below is the snippet.
class func viewControllerWithName(name: String) -> UIViewController?
{
let storyboard = mainStoryboard()
let viewController: AnyObject! = storyboard.instantiateViewControllerWithIdentifier(name)
return viewController as? UIViewController
}
func setUpController {
let viewController: AnyObject!
if self.user() == "admin" {
viewController:AdminViewController = self.viewControllerWithName("admin") !as! AdminViewController
} else {
viewController:UserViewController = self.viewControllerWithName("user") !as! UserViewController
}
self.addChildViewController(viewController)
viewController.view.frame = container.frame
self.view.addSubview(viewController.view)
viewController.didMoveToParentViewController(self)
}
I tried many ways, but, I am unable to typecast viewController. I get error, saying
Cannot invoke 'addChildViewController' with an argument list of type '(AnyObject!)'
What is the right format to achieve this?
Why do you declare it as AnyObject when you know its going to be a view controller? declare it as UIViewController and the error you've mentioned should go away.
You don't need to do viewController:AdminViewController in the if-else section. You are anyways force typecasting the output of viewControllerWithName method.
In this case you are indicating that viewController is AnyObject type:
let viewController: AnyObject!
To pass a UIVIewController to addChildViewController you can try something like:
let viewController = self.viewControllerWithName("admin") as! AdminViewController
self.addChildViewController(viewController)
In swift you cant change a let or var type at runtime
Update your code and change AnyObject to UIViewController:
class func viewControllerWithName(name: String) -> UIViewController?
{
let storyboard = mainStoryboard()
let viewController = storyboard.instantiateViewControllerWithIdentifier(name) as? UIViewController
return viewController
}
func setUpController {
let viewController: UIViewController!
if self.user() == "admin" {
viewController = self.viewControllerWithName("admin")!
} else {
viewController = self.viewControllerWithName("user")!
}
self.addChildViewController(viewController)
viewController.view.frame = container.frame
self.view.addSubview(viewController.view)
viewController.didMoveToParentViewController(self)
}

Swift/iOS Controller Does not Retain Delegate when Passed to a Function

I have a UITableViewController subclass called LogbookFormTVC that conforms to UIPopoverPresentationControllerDelegate. In this class I have a function that creates and shows a popover:
// --------------------
// LogbookFormTVC.swift
// --------------------
class LogbookFormTVC: UITableViewController, UIAdaptivePresentationControllerDelegate, UIPopoverPresentationControllerDelegate {
#IBAction func tapShowPopover(sender: AnyObject) {
//Tap to show the popover
self.presentViewController(showAircraftPicker(), animated: true, completion: nil)
}
//Build the popover
func showAircraftPicker() -> UIViewController{
//Set up modal
let storyboard = UIStoryboard(name: "Popovers", bundle: nil)
var aircraftModal = storyboard.instantiateViewControllerWithIdentifier("AircraftModal") as! AircraftPickerVC
let pc = aircraftModal.popoverPresentationController
pc?.sourceView = self.view
pc?.delegate = self
return aircraftModal
}
}
I want to move this showAircraftPicker() function and make it available anywhere in my app, so I move it to another file like this:
// --------------------
// SomeWhereElse.swift
// --------------------
//This works
func showAircraftPicker(controller: LogbookFormTVC) -> UIViewController{
//Set up modal
let storyboard = UIStoryboard(name: "Popovers", bundle: nil)
var aircraftModal = storyboard.instantiateViewControllerWithIdentifier("AircraftModal") as! AircraftPickerVC
let pc = aircraftModal.popoverPresentationController
pc?.sourceView = self.view
pc?.delegate = self
return aircraftModal
}
Note how I have to set the type of controller to LogbookFormTVC in order for its protocol conformity to come in with it. But I want this function to work with any class (that conforms to the right protocol, of course).
So doing this doesn't work:
func showAircraftPicker(controller: AnyObject) -> UIViewController{
//Set up modal
let storyboard = UIStoryboard(name: "Popovers", bundle: nil)
var aircraftModal = storyboard.instantiateViewControllerWithIdentifier("AircraftModal") as! AircraftPickerVC
let pc = aircraftModal.popoverPresentationController
pc?.sourceView = self.view
pc?.delegate = self <-- !!! Type AnyObject does not conform to protocol UIPopoverPresentationControllerDelegate !!!
return aircraftModal
}
How can I make this function work with any class and pass on that class's protocol conformity?
You could try to create and extension for UIViewController like this:
extension UIViewController {
func showAircraftPicker(delegate: UIPopoverPresentationControllerDelegate) {
let storyboard = UIStoryboard(name: "Popovers", bundle: nil)
var aircraftModal = storyboard.instantiateViewControllerWithIdentifier("AircraftModal") as! AircraftPickerVC
let pc = aircraftModal.popoverPresentationController
pc?.sourceView = self.view
pc?.delegate = delegate
return aircraftModal
}
}
Note how I have to set the type of controller to LogbookFormTVC in order for its protocol conformity to come in with it. But I want this function to work with any class (that conforms to the right protocol, of course).
Great. So pass an object of the type "conforms to the right protocol:"
func showAircraftPicker(controller: UIPopoverPresentationControllerDelegate) -> UIViewController{
This is exactly what protocols exist to allow you to do.
If you want to conform to multiple restrictions simultaneously, a generic is handy:
func showAircraftPicker<T: UIViewController
where T:UIPopoverPresentationControllerDelegate>(controller: T) -> UIViewController {

Resources