Use internal func from a class to another class in swift - ios

I have a profile class and settings class
profile class contains an internal function
class Profile: UIViewController,UIImagePickerControllerDelegate,UINavigationControllerDelegate {
internal func profileSelectFromGallery(sender: Profile){
let myPickerController = UIImagePickerController()
myPickerController.delegate = sender;
myPickerController.sourceType =
UIImagePickerControllerSourceType.PhotoLibrary
sender.presentViewController(myPickerController, animated:true, completion: nil)
}
}
I want to use profileSelectFromGallery in setting class and I have two tries below
class SettingsVC: UITableViewController {
// I will call this private function on a click events
private func selectFromGallery(){
// let profile = Profile()
// profile.profileSelectFromGallery(self)
Profile.profileSelectFromGallery(self)
}
}
The above codes results into Cannot convert value of type 'SettingsVC' to expected argument type 'Profile' since profileSelectFromGallery needs a parameter of a class Profile so what i want to do is change sender so that i can use it from any of my class and not just my Profile class.

So the problem is that you can't convert a SettingsVC into a Profile. If you look at the method signature you'll see it's expecting a Profile:
internal func profileSelectFromGallery(sender: Profile)
You are trying to pass in a SettingVC in selectFromGallery()
Inside profileSelectFromGallery you want the sender to be both a UIViewController and a UIImagePickerControllerDelegate. There's a couple ways you could do this:
The simplest is to change the method signature. You'd do something like this:
internal func profileSelectFromGallery(sender: UIImagePickerControllerDelegate){
guard let vc = sender as? UIViewController else {
return
}
let myPickerController = UIImagePickerController()
myPickerController.delegate = sender;
myPickerController.sourceType =
UIImagePickerControllerSourceType.PhotoLibrary
vc.presentViewController(myPickerController, animated:true, completion: nil)
}
Theres 2 main things here: sender is changed to the proper delegate method, and theres a guard statement to cast it to a VC for the presentViewController call.
The much more awesome way to do this is to use protocol extensions!
extension UIImagePickerControllerDelegate where Self: UIViewController, Self: UINavigationControllerDelegate {
func profileSelectFromGallery() {
let myPickerController = UIImagePickerController()
myPickerController.delegate = self
myPickerController.sourceType = UIImagePickerControllerSourceType.PhotoLibrary
self.presentViewController(myPickerController, animated:true, completion: nil)
}
}
Basically what I'm doing here is adding a method for every UIImagePickerControllerDelegate thats also an UIViewController and an UINAvigationControllerDelegate. This means that I can call it on both Profile and SettingVC (once you add the necessary delegates to SettingVC). All you would need to do is:
let profile = Profile()
profile.profileSelectFromGallery()
let settingVC = SettingVC()
settingVC.profileSelectFromGallery()

Declare new protocol as:
protocol PickerProtocol : UIImagePickerControllerDelegate, UINavigationControllerDelegate {
}
Now your Profile class will look like:
class Profile: UIViewController, PickerProtocol {
//Option 1
internal func profileSelectFromGallery(contoller: UIViewController, pickerProtocol: PickerProtocol){
let myPickerController = UIImagePickerController()
myPickerController.delegate = pickerProtocol
myPickerController.sourceType =
UIImagePickerControllerSourceType.PhotoLibrary
contoller.presentViewController(myPickerController, animated:true, completion: nil)
}
//Option 2
internal func profileSelectFromGalleryOption2(sender : UIViewController? ) {
var viewContoller : UIViewController = self
if let unwrappedSender = sender {
viewContoller = unwrappedSender
}
let myPickerController = UIImagePickerController()
if let pickerProtocol = viewContoller as? PickerProtocol {
myPickerController.delegate = pickerProtocol
} else {
myPickerController.delegate = self //Assign self as default
}
myPickerController.sourceType = UIImagePickerControllerSourceType.PhotoLibrary
viewContoller.presentViewController(myPickerController, animated:true, completion: nil)
}
}
class SettingsVC1: UITableViewController {
// I will call this private function on a click events
private func selectFromGallery(){
let profile = Profile()
profile.profileSelectFromGallery(self, pickerProtocol:profile)
profile.profileSelectFromGalleryOption2(self)
//Or
profile.profileSelectFromGalleryOption2(nil)//profile itself delegate and presenting controller
}
}
// OR
class SettingsVC2: UITableViewController, PickerProtocol {
// I will call this private function on a click events
private func selectFromGallery(){
let profile = Profile()
profile.profileSelectFromGallery(self, pickerProtocol:self)
profile.profileSelectFromGalleryOption2(self)
//Or
profile.profileSelectFromGalleryOption2(nil)//profile itself delegate and presenting controller
}
}

I would use POP (protocol oriented programming) like this:
protocol SelectorProtocol: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
}
extension SelectorProtocol where Self: UIViewController {
func profileSelectFromGallery() {
let myPickerController = UIImagePickerController()
myPickerController.delegate = self;
myPickerController.sourceType = UIImagePickerControllerSourceType.PhotoLibrary
self.presentViewController(myPickerController, animated:true, completion: nil)
}
}
class Profile: UIViewController,UIImagePickerControllerDelegate,UINavigationControllerDelegate, SelectorProtocol {
func foo() {
profileSelectFromGallery()
}
}
class SettingsVC: UITableViewController, SelectorProtocol {
// I will call this private function on a click events
private func selectFromGallery(){
profileSelectFromGallery()
}
}

You are trying to statically call profileSelectFromGallery: even though it is an instance method.
Try changing the method definition to:
internal static func profileSelectFromGallery(sender: Profile){
As for being able to use any class as a delegate, create a custom Protocol and ensure that the sender conforms to this protocol. See here (specifically the heading titled Protocols) for more information: http://www.raywenderlich.com/115300/swift-2-tutorial-part-3-tuples-protocols-delegates-and-table-views

perhaps the following will work:
class SettingsVC: UITableViewController {
// I will call this private function on a click events
private func selectFromGallery(){
let prof = Profile()
prof.profileSelectFromGallery(prof)
}
}

Related

use popToRootViewController and pass Data

I'm applying for a junior developer position and I've got a very specific task, that already took me 3 days to complete. Sounds easy - pass data to rootViewController.
That's what I've done:
1)
private func userDefaultsToRootController() {
let input = textField.text!
defaults.set(input, forKey: "SavedLabel")
navigationController?.popViewController(animated: true)
}
private func segueToRootViewController() {
let destinationVC = MainScreen1()
let input = textField.text!
if input == "" { self.navigationController?.popToRootViewController(animated: true) }
destinationVC.input = input
navigationController?.pushViewController(destinationVC, animated: true)
}
private func popToNavigationController() {
let input = textField.text!
if let rootVC = navigationController?.viewControllers.first as? MainScreen1 {
rootVC.input = input
}
navigationController?.popToRootViewController(animated: true)
}
I've used CoreData
But here is the difficult part - I've got an email, that all these methods are not good enough and I need to use delegate and closure. I've done delegation and closures before, but when I popToRootViewController delegate method passes nil. Could you at least point where to find info about this?
** ADDED **
There are 2 View Controllers: Initial and Second one.
That's what I have in the Initial View Controller:
var secondVC = MainScreen2()
override func viewDidLoad() {
super.viewDidLoad()
secondVC.delegate = self
}
That's how I push SecondViewController
#objc private func buttonTapped(_ sender: CustomButton) {
let nextViewController = MainScreen2()
navigationController?.pushViewController(nextViewController, animated: true)
}
In SecondViewController I've got this protocol
protocol PassData {
func transferData(text: String)
}
Also a delegate:
var delegate: PassData?
This is how I go back to initial view controller
#objc private func buttonTapped(_ sender: CustomButton) {
if let input = textField.text {
print(input)
self.delegate?.transferData(text: input)
self.navigationController?.popToRootViewController(animated: true)
}
}
Back to the Initial view controller where I've implemented delegate method
extension MainScreen1: PassData {
func transferData(text: String) {
print("delegate called")
label.text = text
}
}
Delegate doesn't get called.
BASED ON YOUR EDIT:
You must set the delegate in buttonTapped
#objc private func buttonTapped(_ sender: CustomButton) {
let nextViewController = MainScreen2()
nextViewController.delegate = self // HERE WHERE YOU SET THE DELEGATE
navigationController?.pushViewController(nextViewController, animated: true)
}
You can delete the second instance and your code in viewDidLoad. That's not the instance you push.
This should point you in the right direction to use delegation and completion handler.
protocol YourDelegateName {
func passData(data:YourDataType)
}
class SecondViewController: UIViewController {
var delegate: YourDelegateName?
func passDataFromSecondViewController(){
YourCoreDataClass.shared.getCoreData { (yourStringsArray) in
self.delegate?.passData(data: yourStringsArray)
self.navigationController?.popToRootViewController(animated: true)
}
}
class InitialViewController: UIViewController, YourDelegateName {
override func viewDidLoad() {
super.viewDidLoad()
// or whenever you instantiate your SecondViewController
let secondViewController = SecondViewController()
secondViewController.delegate = self //VERY IMPORTANT, MANY MISS THIS
self.navigationController?.pushViewController(createVC, animated: true)
}
func passData(data:YourDataType){
//user your data
}
}
class YourCoreDataClass: NSObject {
static let shared = YourCoreDataClass()
func getCoreData (completion: ([String]) -> ()){
........... your code
let yourStringsArray = [String]() // let's use as example an array of strings
//when you got the data your want to pass
completion(yourStringsArray)
}
}

How to load a UIViewController from a class that does not inherit UIviewController (from a swift file with class )?

I am working on building framework for a widget. I have two files as follows:
widgetBuilder.swift and
webWidget.swift
widgetBuilder.swift is file with getters and setters that takes certain values from the app that is going to use this widget framework.
Code from widgetBuilder.swift
import Foundation
class WidgetBuilder {
private var userId: String!
private var emailId: String!
public func setuserId(userId: String) -> WidgetBuilder{
self.userId = userId
return self
}
public func setEmailId(emailId: String) -> WidgetBuilder{
self.emailId = emailId
return self
}
public func build() -> WidgetBuilder{
// Wanted to load the webview from here
}
}
Once the initialization is done I would call the build function, I wanted to load the ViewController of webWidget.swift
Code from webWidget.swift
class webWidget: UIViewController, WKUIDelegate, WKNavigationDelegate {
var webView: WKWebView!
override func loadView() {
webView = WKWebView()
webView.uiDelegate = self
webView.navigationDelegate = self
view = webView
self.loadWebView()
}
public func loadWebView(){
let url = URL(string: "https://www.google.com")!
webView.load(URLRequest(url: url))
webView.allowsBackForwardNavigationGestures = true
}
public func loadWidgetScreen() {
//Something is not correct here
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: "WidgetController")
self.present(controller, animated: true, completion: nil)
}
}
How do I load the webWidget view from the widgetBuilder.swift and pass some data along ?
So this is how I solved , Posting it, might be helpful for somebody looking to load a ViewController from a swift file.
Once I call the the build() from my swift file , it has to load the view,
Added a var that gets the ViewController instance from the parent view that wants to load this widget.
Inside the widgetBuilder.swift
private var clientView: UIViewController?
public init(_ viewController:UIViewController){
self.clientView = viewController;
}
Then,
public func build(){
// Widget() creates ViewController instance , this viewcontroller is mapped to a widget.xib file
let controller: UIViewController = Widget() // Creating the instance of the view class
self.clientView?.addChild(controller) // self.clientView is the ViewController instance that wants to load widget view
self.clientView?.view.addSubview(controller.view)
controller.view.frame = (clientView?.view.bounds)!
controller.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
controller.didMove(toParent: clientView)
}
In webWidget.swift
replace your loadWidgetScreen() method with below
public func loadWidgetScreen() {
if var topController = UIApplication.sharedApplication().keyWindow?.rootViewController {
while let presentedViewController = topController.presentedViewController {
topController = presentedViewController
}
// topController should now be your topmost view controller
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: "WidgetController")
self.present(controller, animated: true, completion: nil)
}
else {
print("topViewController not found")
}
}

Objects of presenting/showing ViewController equal to nil

This func is used for showing/presenting ViewController.
let noteViewController = NoteViewController()
extension NoteTableViewController: UIViewControllerTransitioningDelegate {
func remindLater() {
if let note = notificationNote?.copy() as? Note {
noteViewController.transitioningDelegate = self
noteViewController.modalPresentationStyle = UIModalPresentationStyle.fullScreen
noteViewController.modalTransitionStyle = UIModalTransitionStyle.coverVertical
resultSearchController.searchBar.isHidden = true
present(noteViewController, animated: true, completion: nil)
//show(noteViewController, sender: self)
}
}
}
On the ViewController i am trying to present i've an outlet for UIDatePicker object. On viewdidLoad() method i have this line that causes an error.
frameDatePicker = remindDatePicker.frame
So, i found remindDatePicker and other objects equal to nil.
Where should i find a mistake?

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/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