-1

I have an app where at first a map is shown with all companies close to my position. Main screen has a search button where I can click and filter results according to my specific needs enter image description here

The connection between Mapa View Controller and Filtrar View Controller was built based on a custom protocol (according to https://medium.com/@jamesrochabrun/implementing-delegates-in-swift-step-by-step-d3211cbac3ef).

All the communication works fine and I'm able to filter and present companies that meet the criteria. My issue is when the filter returns nothing (there are no companies that meet the criteria). When that happen I wanted to present a UIAltert as an advice to the end user.

Alert is triggered but I get an error message "Attempt to present on which is already presenting (null)".

I tried one of the suggestions from What is the best way to check if a UIAlertController is already presenting?

   if self.presentedViewController == nil {
       // do your presentation of the UIAlertController
       // ...
    } else {
       // either the Alert is already presented, or any other view controller
       // is active (e.g. a PopOver)
       // ...

       let thePresentedVC : UIViewController? = self.presentedViewController as UIViewController?

       if thePresentedVC != nil {
          if let thePresentedVCAsAlertController : UIAlertController = thePresentedVC as? UIAlertController {
             // nothing to do , AlertController already active
             // ...
             print("Alert not necessary, already on the screen !")

          } else {
             // there is another ViewController presented
             // but it is not an UIAlertController, so do 
             // your UIAlertController-Presentation with 
             // this (presented) ViewController
             // ...
             thePresentedVC!.presentViewController(...)

             print("Alert comes up via another presented VC, e.g. a PopOver")
          }
      }
    }

I see "Alert comes up via another presented VC, e.g. a PopOver" printed but I don't know how to use thePresentedVC!.presentViewController(...).

I'm using XCode 11.2.1 and IOS 11.

My detailed code is

Alert Class

import UIKit

class Alerta {
    var titulo : String
    var mensagem : String

    init(titulo: String, mensagem: String) {
        self.titulo = titulo
        self.mensagem = mensagem

    }

    func getAlerta() -> UIAlertController {
        let alerta = UIAlertController(title: titulo, message: mensagem, preferredStyle: .alert)
        let acaoCancelar = UIAlertAction(title: "Ok", style: .cancel, handler: nil)

        alerta.addAction(acaoCancelar)
        return alerta
    }
}

Mapa Class

import UIKit
import MapKit
import ProgressHUD

class MapaViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate, FiltroVCDelegate {

    @IBOutlet weak var mapa: MKMapView!
    var gerenciadorLocalizacao = CLLocationManager()
    var anotacaoArray = [MinhaAnotacao]()
    // var annotationSearch: [MinhaAnotacao] = [] // Filtered annotation
    var anotacao = MinhaAnotacao()
    var searchArr: [String] = []
    var searching = false

    var todasAnotacoes: [(objLat: CLLocationDegrees, objLong: CLLocationDegrees, objName: String, objDesc: String, objId: String, objTel1: String, objTel2: String, objEsp1: String, objEsp2: String, objEst: String, objCid: String)] = []
    var clinicasR: [(objLat: CLLocationDegrees, objLong: CLLocationDegrees, objName: String, objDesc: String, objId: String, objTel1: String, objTel2: String, objEsp1: String, objEsp2: String, objEst: String, objCid: String)] = []

    @IBOutlet weak var adicionarOutlet: UIBarButtonItem!

    override func viewDidLoad() {
        super.viewDidLoad()

        mapa.register(MyAnnotationView.self, forAnnotationViewWithReuseIdentifier: MKMapViewDefaultAnnotationViewReuseIdentifier)

        // Retrieving Logger user data and hidding "add" button if applicable
        ProgressHUD.show("Carregando...")
        var searching = false


        // Map - User location
        self.mapa.delegate = self
        self.gerenciadorLocalizacao.delegate = self
        self.gerenciadorLocalizacao.desiredAccuracy = kCLLocationAccuracyBest
        self.gerenciadorLocalizacao.requestWhenInUseAuthorization()
        self.gerenciadorLocalizacao.startUpdatingLocation()

        ProgressHUD.dismiss()
    }

    // add annotations to the map
    func addAnnotationsToMap() {
        anotacaoArray = []
        self.mapa.removeAnnotations(mapa.annotations)
        if searching {
            for oneObject in self.clinicasR {
                let umaAnotacao = MinhaAnotacao()
                let oneObjLoc: CLLocationCoordinate2D = CLLocationCoordinate2DMake(oneObject.objLat, oneObject.objLong)
                umaAnotacao.coordinate = oneObjLoc
                umaAnotacao.title = oneObject.objName
                umaAnotacao.subtitle = oneObject.objDesc
                umaAnotacao.identicadorMapa = oneObject.objId
                umaAnotacao.telefone = oneObject.objTel1
                umaAnotacao.telefone2 = oneObject.objTel2
                umaAnotacao.especialidade1 = oneObject.objEsp1
                umaAnotacao.especialidade2 = oneObject.objEsp2
                umaAnotacao.estado = oneObject.objEst
                umaAnotacao.cidade = oneObject.objCid
                umaAnotacao.endereco = oneObject.objDesc
                umaAnotacao.latitude = String(oneObject.objLat)
                umaAnotacao.longitude = String(oneObject.objLong)

                self.anotacaoArray.append(umaAnotacao)
            }

        } else {
            for oneObject in self.todasAnotacoes {
                let umaAnotacao = MinhaAnotacao()
                let oneObjLoc: CLLocationCoordinate2D = CLLocationCoordinate2DMake(oneObject.objLat, oneObject.objLong)
                umaAnotacao.coordinate = oneObjLoc
                umaAnotacao.title = oneObject.objName
                umaAnotacao.subtitle = oneObject.objDesc
                umaAnotacao.identicadorMapa = oneObject.objId
                umaAnotacao.telefone = oneObject.objTel1
                umaAnotacao.telefone2 = oneObject.objTel2
                umaAnotacao.especialidade1 = oneObject.objEsp1
                umaAnotacao.especialidade2 = oneObject.objEsp2
                umaAnotacao.estado = oneObject.objEst
                umaAnotacao.cidade = oneObject.objCid
                umaAnotacao.endereco = oneObject.objDesc
                umaAnotacao.latitude = String(oneObject.objLat)
                umaAnotacao.longitude = String(oneObject.objLong)

                self.anotacaoArray.append(umaAnotacao)
            }
        }
        self.mapa.addAnnotations(self.anotacaoArray)
    }

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "clinicaDetailsSegue" {
            let clinicasDetailsViewController = segue.destination as! ClinicasDetailsViewController
            clinicasDetailsViewController.identificador = self.anotacao.identicadorMapa
        } else if segue.identifier == "searchSegue" {
            if let nav = segue.destination as? UINavigationController, let filterVC = nav.topViewController as? FiltrarViewController {
                filterVC.delegate = self
            }
        }
    }

    func search(searchAnnotation: [MinhaAnotacao], searchArr: [String]) -> [MinhaAnotacao] {
        var searchArr = searchArr
        // base case - no more searches - return clinics found
        if searchArr.count == 0 {
            return searchAnnotation
        }
        // itterative case - search clinic with next search term and pass results to next search
        let foundAnnotation = searchAnnotation.filter { item in
            (item.title!.lowercased() as AnyObject).contains(searchArr[0]) ||
                item.especialidade1.lowercased().contains(searchArr[0]) ||
                item.especialidade2.lowercased().contains(searchArr[0]) ||
                item.cidade.lowercased().contains(searchArr[0]) ||
                item.estado.lowercased().contains(searchArr[0]) ||
                item.latitude.contains(searchArr[0]) || item.longitude.contains(searchArr[0]) || item.endereco.contains(searchArr[0])
        }
        // remove completed search and call next search
        searchArr.remove(at: 0)
        return search(searchAnnotation: foundAnnotation, searchArr: searchArr)
    }

    // From Custom Protocol
    func dadosEscolhidos(nomeClinicaFiltro: String, estadoClinicaFiltro: String, cidadeClinicaFiltro: String, especialidade1ClinicaFiltro: String, especialidade2ClinicaFiltro: String) {

        searchArr = []
        clinicasR = []
        searchArr = ["\(nomeClinicaFiltro.lowercased())","\(estadoClinicaFiltro.lowercased())", "\(cidadeClinicaFiltro.lowercased())", "\(especialidade1ClinicaFiltro.lowercased())", "\(especialidade2ClinicaFiltro.lowercased())"]
        searchArr = searchArr.filter({ $0 != ""})

        let annotationSearch = search(searchAnnotation: anotacaoArray, searchArr: searchArr) // Filtered Clinicas

        if annotationSearch.count > 0 {
            for i in 0...annotationSearch.count - 1 {
                self.clinicasR.append((objLat: Double(annotationSearch[i].latitude)!, objLong: Double(annotationSearch[i].longitude)!, objName: annotationSearch[i].title!, objDesc: annotationSearch[i].endereco, objId: annotationSearch[i].identicadorMapa, objTel1: annotationSearch[i].telefone, objTel2: annotationSearch[i].telefone2, objEsp1: annotationSearch[i].especialidade1, objEsp2: annotationSearch[i].especialidade2, objEst: annotationSearch[i].estado, objCid: annotationSearch[i].cidade))
            }

            searching = true
            addAnnotationsToMap()
        } else {

            if self.presentedViewController == nil {
               let alerta = Alerta(titulo: "Erro", mensagem: "Nenhuma clínica atende ao filtro definido")

               self.present(alerta.getAlerta(), animated: true, completion: nil)
               print( "e Aqui, chegou? \(annotationSearch.count)")
            } else {
               // either the Alert is already presented, or any other view controller
               // is active (e.g. a PopOver)
               // ...

               let thePresentedVC : UIViewController? = self.presentedViewController as UIViewController?

               if thePresentedVC != nil {
                  if let thePresentedVCAsAlertController : UIAlertController = thePresentedVC as? UIAlertController {
                     // nothing to do , AlertController already active
                     // ...
                     print("Alert not necessary, already on the screen !")

                  } else {
                     // there is another ViewController presented
                     // but it is not an UIAlertController, so do
                     // your UIAlertController-Presentation with
                     // this (presented) ViewController
                     // ...
                    //let alerta = Alerta(titulo: "Erro", mensagem: "Nenhuma clínica atende ao filtro definido")
                    //thePresentedVC!.presentedViewController(alerta)

                     print("Alert comes up via another presented VC, e.g. a PopOver")
                  }
              }
            }
        }
}
}

Filtrar View Controller

import UIKit
import ProgressHUD

protocol FiltroVCDelegate: class {
    func dadosEscolhidos(nomeClinicaFiltro: String, estadoClinicaFiltro: String, cidadeClinicaFiltro: String, especialidade1ClinicaFiltro: String, especialidade2ClinicaFiltro: String)
}


class FiltrarViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {

    weak var delegate: FiltroVCDelegate?

    var nomeSelecionado = ""

    var estadosJSON = [Estado]()
    var cidades = [Cidade]()
    var estado : Estado? // Selected State identifier
    var cidade : Cidade? // Selected City identifier
    var estadoSelecionado = "" // Selected State
    var cidadeSelecionada = "" // Selected City

    var especialidadesJSON = [Especialidade]()
    var especialidades2 = [Especialidade2]()
    var especialidade1 : Especialidade? // Selected Specialty1 identifier
    var especialidade2 : Especialidade2? // Selected Specialty2 identifier
    var especialidade1Selecionada = ""
    var especialidade2Selecionada = ""

    let fontName = "HelveticaNeue"
    var searchArr = [String]()

    @IBOutlet weak var nomeClinica: UITextField!
    @IBOutlet weak var especialidadeLabel: UILabel!

    @IBOutlet weak var estadoClinicaPicker: UIPickerView!
    @IBOutlet weak var especialidade1Picker: UIPickerView!
    @IBOutlet weak var especialidade2Picker: UIPickerView!

    override func viewDidLoad() {
        ProgressHUD.show("Carregando...")
        readJsonEstados()
        readJsonEspecialidades()
        super.viewDidLoad()
        nomeClinica.text = ""
        especialidadeLabel.transform = CGAffineTransform(rotationAngle: -CGFloat.pi / 2)
        ProgressHUD.dismiss()

    }


    @IBAction func aplicarFiltro(_ sender: Any) {
        if nomeClinica.text == nil {
            nomeClinica.text = ""
        }

        delegate?.dadosEscolhidos(nomeClinicaFiltro: nomeClinica.text!, estadoClinicaFiltro: estadoSelecionado, cidadeClinicaFiltro: cidadeSelecionada, especialidade1ClinicaFiltro: especialidade1Selecionada, especialidade2ClinicaFiltro: especialidade2Selecionada)
        navigationController?.dismiss(animated: true)

    }

    func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {

        especialidade1Picker.reloadComponent(0)
        especialidade2Picker.reloadComponent(0)
        estadoClinicaPicker.reloadAllComponents()

        if pickerView == estadoClinicaPicker {
            if component == 0 {
                self.estado = self.estadosJSON[row]
                self.cidades = self.estadosJSON[row].cidades
                estadoClinicaPicker.reloadComponent(1)
                estadoClinicaPicker.selectRow(0, inComponent: 1, animated: true)
            } else {
                self.cidade = self.cidades[row]
                estadoClinicaPicker.reloadAllComponents()
            }
        } else if pickerView == especialidade1Picker {
            self.especialidade1 = self.especialidadesJSON[row]
            self.especialidades2 = self.especialidadesJSON[row].especialidade2
            especialidade1Picker.reloadComponent(0)
            especialidade2Picker.reloadComponent(0)
            especialidade2Picker.selectRow(0, inComponent: 0, animated: true)

        } else if pickerView == especialidade2Picker {
            self.especialidade2 = self.especialidades2[row]
            especialidade1Picker.reloadComponent(0)
            especialidade2Picker.reloadComponent(0)
        }

        let estadoIndiceSelecionado = estadoClinicaPicker.selectedRow(inComponent: 0)
        let cidadeIndiceSelecionada = estadoClinicaPicker.selectedRow(inComponent: 1)
        let especialidade1IndiceSelecionado = especialidade1Picker.selectedRow(inComponent: 0)
        let especialidade2IndiceSelecionado = especialidade2Picker.selectedRow(inComponent: 0)

        if estadoIndiceSelecionado >= 0 {
            if cidadeIndiceSelecionada >= 0 {
                estadoSelecionado = estadosJSON[estadoIndiceSelecionado].nome
                cidadeSelecionada = cidades[cidadeIndiceSelecionada].nome
            }
        }

        if especialidade1IndiceSelecionado >= 0 {
            if especialidade2IndiceSelecionado >= 0 {
                especialidade1Selecionada = especialidadesJSON[especialidade1IndiceSelecionado].nome
                especialidade2Selecionada = especialidadesJSON[especialidade1IndiceSelecionado].especialidade2[especialidade2IndiceSelecionado].nome
            }
        }
    }

    func numberOfComponents(in pickerView: UIPickerView) -> Int {
        if pickerView == estadoClinicaPicker {
            return 2
        } else if pickerView == especialidade1Picker {
            return 1
        } else if pickerView == especialidade2Picker {
            return 1
        }
        return 1
    }

    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        if pickerView == estadoClinicaPicker {
            if component == 0 {
                return estadosJSON.count
            } else {
                return cidades.count
            }
        } else if pickerView == especialidade1Picker {
            return self.especialidadesJSON.count

        } else if pickerView == especialidade2Picker {
            return especialidades2.count
        }
        return 1
    }

    func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView {
        var rowTitle = ""
        let pickerLabel = UILabel()

        pickerLabel.textColor = UIColor.black

        if pickerView == estadoClinicaPicker {
            if component == 0 {
                rowTitle = estadosJSON[row].nome
            } else {
                rowTitle = cidades[row].nome
            }
        } else if pickerView == especialidade1Picker {
            rowTitle = especialidadesJSON[row].nome
        } else if pickerView == especialidade2Picker {
            rowTitle = especialidades2[row].nome
        }

        pickerLabel.text = rowTitle
        pickerLabel.font = UIFont(name: fontName, size: 16.0)
        pickerLabel.textAlignment = .center

        return pickerLabel
    }

    func pickerView(_ pickerView: UIPickerView, widthForComponent component: Int) -> CGFloat {
        if pickerView == estadoClinicaPicker {
            if component == 0 {
                return 50
            } else {
                return 300
            }
        }
        return 300
    }


    @IBAction func cancel(_ sender: Any) {
        navigationController?.dismiss(animated: true)
   }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        view.endEditing(true)
    }

    func readJsonEstados() {
        let url = Bundle.main.url(forResource: "EstadosECidades", withExtension: "json")!
        do {
            let data = try Data(contentsOf: url)
            let jsonResult = try JSONDecoder().decode(RootState.self, from: data)

            //handles the array of countries on your json file.
            self.estadosJSON = jsonResult.estado
            self.cidades = self.estadosJSON.first!.cidades

        } catch {
            let alerta = Alerta(titulo: "Erro ao Carregar", mensagem: "Erro ao carregar Estados e Cidades. Por favor reinicie o app")
            self.present(alerta.getAlerta(), animated: true, completion: nil)
        }
    }

    func readJsonEspecialidades() {
        let url = Bundle.main.url(forResource: "Especialidades", withExtension: "json")!
        do {
            let data = try Data(contentsOf: url)
            let jsonResult = try JSONDecoder().decode(RootEsp.self, from: data)

            //handles the array of specialties on your json file.
            self.especialidadesJSON = jsonResult.especialidade
            self.especialidades2 = self.especialidadesJSON.first!.especialidade2
        } catch {
            let alerta = Alerta(titulo: "Erro ao Carregar", mensagem: "Erro ao carregar Especialidades. Por favor reinicie o app")
            self.present(alerta.getAlerta(), animated: true, completion: nil)
        }
    }

    /*
     // MARK: - Navigation

     // In a storyboard-based application, you will often want to do a little preparation before navigation
     override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
     // Get the new view controller using segue.destination.
     // Pass the selected object to the new view controller.
     }
     */

}

Thanks EDIT

Link to the project https://github.com/afernandes0001/UIAlertController. You just need to click Log in (no validation is done), go to main screen, Map, Click Search and Apply - There is no need to add any data as I have made data static.

  • you want `class func show(arguments)` - the class keyword makes the func a member of the Type – froggomad Dec 08 '19 at 19:38
  • thanks froggomad. Sorry for the very basic question but I have a little bit less than 60 days in Swift and I'm even trying to learn how to search for thing. Sometimes it is hard even to define the best search criteria. Anyway, even adding "class", the solution hasn't worked – André Fernandes Dec 08 '19 at 19:59
  • I edited my answer to show the class definition as well. This is the standard way I show alerts in my projects, so it should work assuming you're calling the alert from somewhere in the ViewController's lifecycle where it's able to present (i.e. after the view has appeared) – froggomad Dec 08 '19 at 20:07
  • Your code works in general to present a message. In my specific case, the error still persists (Attempt to present on which is already presenting (null)). I use a custom protocol to pass data from one VCB (Filtrar VC) to VCA (Mapa VC). VCB collects data and pass to VCA via "func dadosEscolhidos(nomeClinicaFiltro: String, estadoClinicaFiltro: String, cidadeClinicaFiltro: String, especialidade1ClinicaFiltro: String, especialidade2ClinicaFiltro: String)". Alert is triggered from within the func. – André Fernandes Dec 08 '19 at 20:50
  • Make absolute sure your code isn't being called twice. If not, in viewDidLoad, set `definesPresentationContext = true` - this explicitly sets the vc to present modal views, so the view hierarchy won't be traversed – froggomad Dec 08 '19 at 22:23
  • Thanks but that didn't work as well. I have added a counter and UIAlert call runs only once. I believe the error is related to when/where the alert is triggered. Even though "dadosEscolhidos" is in the Mapa View Controller, it is triggered by Filtrar View Controller. Swift flow is to run code from Filtrar VC, then run dadosEscolhidos in "Mapa VC" (with Filtrar VC shown), go back Filtrar VC and run dismiss that takes us to Mapa VC definitely. I doing research on how to display a UIAlert on another VC. – André Fernandes Dec 09 '19 at 00:58
  • Im understanding now that you're calling the alert from another ViewController. Instead of passing in `vc: self`, you'll have to get an instance of the presenting ViewController and pass it in to the `vc:` parameter. – froggomad Dec 09 '19 at 15:56
  • also make sure `definesPresentationContext = true` is set on the presenting vc. There's a lot that can potentially go wrong with calling a modal view in one VC to be presented in another, so make sure you're not force unwrapping anything in production or you'll get crashes instead of nothing. – froggomad Dec 09 '19 at 16:00

3 Answers3

1
import UIKit

class ViewController: UIViewController {

let button = UIButton(type: .system)

override func viewDidLoad() {
    super.viewDidLoad()
    button.backgroundColor = .black
    button.setTitle("Alert", for: .normal)
    button.setTitleColor(.white, for: .normal)
    button.addTarget(self, action: #selector(handleAlert), for: .touchUpInside)
    button.translatesAutoresizingMaskIntoConstraints = false

    view.addSubview(button)
    button.heightAnchor.constraint(equalToConstant: 50).isActive = true
    button.widthAnchor.constraint(equalToConstant: 100).isActive = true
    button.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
    button.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
    // Do any additional setup after loading the view.
}

@objc fileprivate func handleAlert() {
    OperationQueue.main.addOperation {
        showAlert(titulo: "YourTilte", mensagem: "YourMessage", vc: self)
         print("On main thread: \(Thread.current.isMainThread)")
    }
}

extension UIViewController {
func showAlert(titulo: String, mensagem: String, vc: UIViewController) {
    let alerta = UIAlertController(title: titulo, message: mensagem, preferredStyle: .alert)
    let acaoCancelar = UIAlertAction(title: "Ok", style: .cancel, handler: nil)

    alerta.addAction(acaoCancelar)
    vc.present(alerta, animated: true)
 }
}

enter image description here

enter image description here

it work :)

Fabio
  • 5,432
  • 4
  • 22
  • 24
  • Hi Fabio, indeed. If you are in within the same VC, it works fine. In my case, UIAlert is triggered from a Custom Protocol and that might be the issue. I believe the error is related to when/where the alert is triggered. Even though "dadosEscolhidos" is in the Mapa VC, it is triggered by Filtrar VC. Swift flow is to run code from Filtrar VC, then run dadosEscolhidos in "Mapa VC" (with Filtrar VC shown), go back Filtrar VC and run dismiss that takes us to Mapa VC definitely. I've added a github (simplified) to demo the error. – André Fernandes Dec 09 '19 at 15:18
  • Hi Andrè, your github sample give me an error and don't run on simulator. – Fabio Dec 10 '19 at 08:56
  • 1
    Try to add this on call: OperationQueue.main.addOperation { self.showAlert(titulo: "YourTitle", mensagem: "YourMessage", vc: self) } I add this because my suspect is that alert will show, but not in front of other views... to check this put print("On main thread: \(Thread.current.isMainThread)") in call...Tell me if it work, have a nice day :) – Fabio Dec 10 '19 at 09:03
  • Hi Fabio, sorry for the delay... i was away from home for the last days. Your suggestion did it!! That is the piece i was missing "OperationQueue.main.addOperation" – André Fernandes Dec 13 '19 at 01:09
  • @AndréFernandes THX, I'm proud to help you :) – Fabio Dec 13 '19 at 08:21
0

Try passing in the VC to your Alert class and presenting from there.

class Alert {
    class func show(titulo: String, mensagem: String, vc: UIViewController) {
        let alerta = UIAlertController(title: titulo, message: mensagem, preferredStyle: .alert)
        let acaoCancelar = UIAlertAction(title: "Ok", style: .cancel, handler: nil)

        alerta.addAction(acaoCancelar)

        //if more than one VC is presenting, or the same one is presenting twice, this might at least tell you which one (assuming it has a title) or when (if the same one is presenting twice)
        print("\(vc.title) is presenting alert")

        vc.present(alerta, animated: true)
    }
}

class ViewController: UIViewController {
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        Alert.show(titulo: "title", mensagem: "this is an alert", vc: self)
    }
}

Call with Alert.show(titulo: "", mensagem: "", vc: self)

If Alert.show(arguments) doesn't work, you may have left the class keyword off of the function. If so, you'll need to create an instance first let alert = Alert() and use alert.show(arguments)

Or you could modify the func to use class properties and create an instance of the class then call instance.show(vc: self) since you already have properties for titulo and mensagem

froggomad
  • 1,747
  • 2
  • 17
  • 40
  • Thanks @froggomad. I tried to to follow your suggestion but i couldn't make it work as expected. For some reason, when I try to Alert.show I get only Alert.show( – André Fernandes Dec 08 '19 at 16:44
  • are you using the class keyword before func? `class func show(arguments) { }` – froggomad Dec 08 '19 at 19:29
  • if not, you could still use it, but would have to instantiate the alert first ie `let alerta = Alert(arguments)` then call the func `alerta.show(arguments)` – froggomad Dec 08 '19 at 19:30
  • Yes but it is not working. It calls "Alert.showAlert(titulo: "teste1", mensagem: "test", vc: self)" but nothing is shown – André Fernandes Dec 08 '19 at 20:00
0

Make it an extension of UIViewControiller like this:

extension UIViewController {
func showAlert(titulo: String, mensagem: String, vc: UIViewController) {
        let alerta = UIAlertController(title: titulo, message: mensagem, preferredStyle: .alert)
        let acaoCancelar = UIAlertAction(title: "Ok", style: .cancel, handler: nil)

        alerta.addAction(acaoCancelar)

        //if more than one VC is presenting, or the same one is presenting twice, this might at least tell you which one (assuming it has a title) or when (if the same one is presenting twice)
        print("\(vc.title) is presenting alert")

        vc.present(alerta, animated: true)
}}

after simply call wherever you want:

showAlert(titulo: "YourTitle", mensagem: "YourMessage", vc: self)
Fabio
  • 5,432
  • 4
  • 22
  • 24
  • HI Fabio, that hasn't worked as well :-( I'm adding a link to a github repository if you want to check https://github.com/afernandes0001/UIAlertController Thanks – André Fernandes Dec 09 '19 at 12:35
  • You just need to click Log in (no validation is done), go to main screen, Map, Click Search and Apply - There is no need to add any data as I have made data static. – André Fernandes Dec 09 '19 at 13:12