Swift - Function in other view controller is not being called (protocol / delegate) - ios

I'm trying to call the function updateProgress from a LoadingDataHelper object but my delegate is not being called. I think the problem is that this LoadingDataHelper is not connected to the LoadingDataViewController (I mean like when you're having a UIView and a viewController).
LoadingDataHelper
protocol LoadingNewDataDelegate: class {
func updateProgress(progress : Float)
}
class LoadingDataHelper: NSObject {
var delegate: LoadingNewDataDelegate?
static let shared = LoadingDataHelper() // shared instance
func loginUser() {
//more code
updateProgressInViewController()
//more code
}
func updateProgressInViewController() {
delegate?.updateProgress(0.3)
}
}
LoadingDataViewController
class LoadingDataViewController: UIViewController, LoadingNewDataDelegate {
let loadingDataHelper: LoadingDataHelper = LoadingDataHelper()
override func viewDidLoad() {
super.viewDidLoad()
loadingDataHelper.delegate = self
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
if (NSUserDefaults.standardUserDefaults().boolForKey("approvedTermsOfUse")) {
self.updateProgress(0.1)
LoadingDataHelper.shared.loginUser()
} else {
self.askForTerms()
}
}
func updateProgress(progress : Float) {
self.progressBar.setProgress(progress, animated: true)
self.progressBar.setNeedsDisplay()
}
}
Is there a way to solve this?

There is some wrong in above codeL:
In LoadingDataViewController, you are creating static property for LoadingDataHelper class and setting delegate to that class. But you are call "loginUser" using direct call.
You need to change like below:
class LoadingDataViewController: UIViewController, LoadingNewDataDelegate {
**//Change to shared initialisation of object//**
let loadingDataHelper: LoadingDataHelper = LoadingDataHelper.shared
override func viewDidLoad() {
super.viewDidLoad()
loadingDataHelper.delegate = self
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
if (NSUserDefaults.standardUserDefaults().boolForKey("approvedTermsOfUse")) {
self.updateProgress(0.1)
**//Call using static object instead of new instance or direct call//**
loadingDataHelper.loginUser()
} else {
self.askForTerms()
}
}
func updateProgress(progress : Float) {
self.progressBar.setProgress(progress, animated: true)
self.progressBar.setNeedsDisplay()
}
}
This will make your controller to get delegate call back.

Related

issue passing data back to first view controller with delegate and protocol (Swift)

So what I'm trying to do is pass a String and an Int back from one ViewController (NewCellViewController) to the previous one (SecondScreenViewController) when I close it. I added a print statement in the method in SecondScreenViewController that is supposed to receive this data, and it didn't print so I guess the method never ran. This is my code (removed some stuff to only include whats relevant):
SecondScreenViewController:
import UIKit
protocol DataDelegate {
func insertEvent(eventString: String, pos: Int)
}
class SecondScreenViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, DataDelegate {
var eventNames = ["event1", "event2", "event3"]
override func viewDidLoad() {
super.viewDidLoad()
advance()
}
//DataDelegate methods
func insertEvent(eventString: String, pos: Int)
{
print("if this prints, it worked")
if pos == -1
{
eventNames.append(eventString)
}
else
{
eventNames.insert(eventString, at: pos)
}
}
#objc func advance()
{
let vc = NewCellViewController(nibName: "NewCellViewController", bundle: nil)
vc.delegate = self
present(vc, animated: true, completion: nil)
}
}
NewCellViewController:
import UIKit
class NewCellViewController: UIViewController {
var delegate:DataDelegate?
#IBOutlet var addEventName: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func addItem() {
insertNewEvent()
dismiss(animated: true, completion: nil)
}
func insertNewEvent()
{
let eventName = addEventName!.text
delegate?.insertEvent(eventString: eventName!, pos: -1) //add different positions
}
}
I have used the same controller name as yours, just to make you understand better.
SecondScreenViewController
import UIKit
class SecondScreenViewController: UIViewController {
var eventNames = ["event1", "event2", "event3"]
override func viewDidLoad() {
super.viewDidLoad()
//advance()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
advance()
}
private func advance() {
// Dont forget to add `Storyboard ID` as "NewCellViewController" on your Main.storyboard.
// See the image attached below.
if let vc = self.storyboard?.instantiateViewController(identifier: "NewCellViewController") as? NewCellViewController {
vc.delegate = self
present(vc, animated: true)
}
}
}
// Better this way.
extension SecondScreenViewController: DataDelegate {
func insertEvent(eventString: String, pos: Int) {
print(eventString, pos)
}
}
NewCellViewController
import UIKit
// Better to create protocols here.
protocol DataDelegate: class {
func insertEvent(eventString: String, pos: Int)
}
class NewCellViewController: UIViewController {
weak var delegate: DataDelegate?
#IBOutlet var addEventName: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func addItem() {
insertNewEvent()
dismiss(animated: true)
}
private func insertNewEvent() {
delegate?.insertEvent(eventString: "your text", pos: -1)
}
}
Hope, this helps.
You can try using segue in the first VC to push the Second VC and pop the Second VC and come back to First VC. This might help you work with the delegate.
And also you can use the UserDefaults to pass and synchronize such values.
Depending on what exactly you want to pass you could use user defaults. Not recommended by many but I feel for simple data it's really quick and effective

i want to triger navigationcontroller when i press button in UIView class

I want to trigger Navigation controller to some other screen when i press the button in UIView class. How can i do this?
//Code for UIView Class in Which Button Iboutlet is created
import UIKit
protocol ButtonDelegate: class {
func buttonTapped()
}
class SlidesVC: UIView {
var delegate: ButtonDelegate?
#IBAction func onClickFinish(_ sender: UIButton) {
delegate?.buttonTapped()
}
#IBOutlet weak var imgProfile: UIImageView!
}
//ViewController Class code in Which Button Protocol will be entertained
class SwipingMenuVC: BaseVC, UIScrollViewDelegate {
var slidesVC = SlidesVC()
override func viewDidLoad() {
super.viewDidLoad()
slidesVC = SlidesVC()
// add as subview, setup constraints etc
slidesVC.delegate = self
}
extension BaseVC: ButtonDelegate {
func buttonTapped() {
self.navigationController?.pushViewController(SettingsVC.settingsVC(),
animated: true)
}
}
A more easy way is to use typealias. You have to write code in 2 places. 1. your viewClass and 2. in your View Controller.
in your SlidesView class add a typealias and define param type if you need otherwise leave it empty.
class SlidesView: UIView {
typealias OnTapInviteContact = () -> Void
var onTapinviteContact: OnTapInviteContact?
#IBAction func buttonWasTapped(_ sender: UIButton) {
if self.onTapinviteContact != nil {
self.onTapinviteContact()
}
}
}
class SwipingMenuVC: BaseVC, UIScrollViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
let slidesView = SlidesView()
slidesView.onTapinviteContact = { () in
// do whatever you want to do on button tap
}
}
You can use the delegate pattern to tell the containing ViewController that the button was pressed and let it handle whatever is needed to do next, The view doesn't really need to know what happens.
A basic example:
protocol ButtonDelegate: class {
func buttonTapped()
}
class SomeView: UIView {
var delegate: ButtonDelegate?
#IBAction func buttonWasTapped(_ sender: UIButton) {
delegate?.buttonTapped()
}
}
class ViewController: UIViewController {
var someView: SomeView
override func viewDidLoad() {
someView = SomeView()
// add as subview, setup constraints etc
someView.delegate = self
}
}
extension ViewController: ButtonDelegate {
func buttonTapped() {
self.showSomeOtherViewController()
// or
let vc = NewViewController()
present(vc, animated: true)
}
}

Call function from child to parent in Swift

I am using xlpagertabstrip and I have a parent view controller which has two children (child1, child2).
In my parent view controller, I show a UIActivityViewIndicator but I want to know how to hide that indicator in my child1.
This is my code:
ParentViewController:
override func viewDidLoad() {
showActivityIndicator()
super.viewDidLoad()
}
func showActivityIndicator() {
//code related to titleview
navigationItem.titleView = titleView
}
func hideActivityIndicator() {
navigationItem.titleView = nil
}
Child1ViewController:
override func viewDidLoad() {
super.viewDidLoad()
call_api()
}
func call_api(){
//code related to api
//if api is ok, I call hideActivityIndicator()
let pctrl = ParentViewController()
pctrl.hideActivityIndicator()
}
But that code does not work. How can I solve that?
Just pass hideActivityIndicator() from the parent to the child and call it when necessary. So whenever you create your child controller do this:
// Parent Controller
childVC.someMethodFromParent = hideActivityIndicator
And in your ChildController do this:
// Child Controller
internal var someProperty: (() -> Void)!
override func viewDidLoad() {
super.viewDidLoad()
call_api()
}
func call_api(){
//code related to api
//if api is ok, I call hideActivityIndicator()
someMethodFromParent()
}
This should work
How about having a ChildViewControllerDelegate? Something like:
class ParentViewController {
func someFunc(){
...
childVC.delegate = self
...
}
}
extension ParentViewController: ChildViewControllerDelegate {
func childViewControllerDidFinishApiCall() {
hideActivityIndicator()
}
}
protocol ChildViewControllerDelegate: class {
func childViewControllerDidFinishApiCall()
}
class ChildViewController {
weak var delegate: ChildViewControllerDelegate?
func call_api(){
//code related to api
let pctrl = ParentViewController()
delegate?.childViewControllerDidFinishApiCall()
}
}

Memory leak in a delegate method

I am following MVP architecture for developing an iOS app.The app is quite simple in which onViewDidLoad() I call a web service which returns me some data and I display that data in a table view.
ViewController:
class A : UIViewController{
var presenter : MyPresenter?
override func viewDidLoad() {
presenter = MyPresenter(delegate:self)
presenter.callWS()
}
}
extension A : Mydelegate{
func onSuccess(){
//this doesnt allow my viewcontroller to deint
tablview.delegate=self
tableview.datasource=self
tableview.reloadData()
}
}
protocol MyDelegate : class{
func onSuccess()
}
class MYPresenter {
weak var delegate : MyDelegate?
init(MyDelegate) {
self.delegate=delegate
}
func callWS(){
delegate.onSuccess()
}
}
This onSucces of MyDelegate does not allow my A viewcontroller to deint
Please let me know what i am doing wrong?
I've slightly modified version of your code and run it in a playground:
import UIKit
import PlaygroundSupport
class A : UITableViewController {
var presenter : MyPresenter?
override func viewDidLoad() {
presenter = MyPresenter(delegate:self)
presenter?.callWS()
let gesture = UITapGestureRecognizer(target: self, action: #selector(dismissOnTap))
view.addGestureRecognizer(gesture)
}
func dismissOnTap() {
dismiss(animated: true)
}
deinit {
print("Bye VC")
}
}
extension A : MyDelegate {
func onSuccess(){
//this doesnt allow my viewcontroller to deint
tableView.delegate = self
tableView.dataSource = self
tableView.reloadData()
}
}
protocol MyDelegate : class {
func onSuccess()
}
class MyPresenter {
weak var delegate : MyDelegate?
init(delegate: MyDelegate) {
self.delegate = delegate
}
func callWS() {
delegate?.onSuccess()
}
deinit {
print("Bye")
}
}
class B: UIViewController {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
let gesture = UITapGestureRecognizer(target: self, action: #selector(showOnTap))
view.addGestureRecognizer(gesture)
}
func showOnTap() {
let vc = A(style: .plain)
present(vc, animated: true)
}
}
let b = B()
b.view.frame = CGRect(x: 0, y: 0, width: 400, height: 600)
PlaygroundPage.current.liveView = b.view
And everything is deallocating properly. I guess the retain cycle is somewhere else and it's hard to find based on provided code.

How to create a base view controller

I would like to have a BaseViewController that subclasses UIViewController, overrides one of it's methods, but also require it's subclasses to implement new ones.
My case is a bit more complex, but this simple case represents my problem:
My BaseViewController would override viewWillAppear to set it's title, but the title string would come from it's subclasses. I thought about some options, not sure if/which one of them is best.
1 - Class with error throwing methods (my current solution):
class BaseViewController: UIViewController {
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
title = getTitle()
}
func getTitle() -> String {
fatalError("Unimplemented method")
}
}
2 - Receive the title in constructor:
class BaseViewController: UIViewController {
var myTitle: String!
convenience init(title titleSent: String) {
self.init(nibName: nil, bundle: nil)
myTitle = sentTitle
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
title = myTitle
}
}
Note that this options gets really bad if there's more parameters to send
I thought that using protocol would be perfect, until I find out that (of course) protocols can't subclass a class.
Didn't anybody do anything like this before? I don't think so, please share your thoughts.
Update
I tried another way, but got stuck in a compiler error, would this ever work?
procotol BaseViewController {
var myTitle: String { get }
}
extension BaseViewController where Self: UIViewController {
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
title = myTitle
}
}
The compiler says Method does not override any method from its superclass.
I usually create protocol in which I declare what would be nice to have in the controllers. Then I check in the base controller if it's actually implemented and if so, just use the values, like this:
protocol ControllerType {
var navigationTitle: String { get }
}
extension ControllerType {
var navigationTitle: String {
return "Default title"
}
}
class BaseViewController: UIViewController {
override func viewDidLoad() {
if let controller = self as? ControllerType {
self.title = controller.navigationTitle
}
}
}
class ViewController: BaseViewController, ControllerType {
var navigationTitle: String {
return "ViewController"
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
Downfall is you have to implement the ControllerType protocol and there's no way to enforce it.
Something similar would work.
class BaseViewController: UIViewController {
override func viewDidLoad() { }
func setTitle(_ title: String) {
self.title = title
}
}
class ViewController: BaseViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.setTitle("ViewController")
}
}

Resources