UIAlertController Already presenting (null) - swift

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

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)
}
}
it work :)

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

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)

Related

How do I pass a scanned barcode ID from first view controller to second View Controller's UILabel?

This is the barcode scanning tutorial I used in my program, so that you have a lot more context when you read my code: Link
Here is what my program does so far: Essentially, when I scan an item's barcode with my phone, the UIAlert pops up with the barcode ID displayed and a button prompting the user to open the "Results" page. This is all fine and good, but how do I pass that same scanned barcode ID into a label on the Result's page? I have been stuck on this for 2 days now, even though it seems like such an easy task.
Any help is much appreciated <3
Here is my relevant code:
ProductCatalog.plist ->
Link to Image
Scanner_ViewController.swift (first View Controller) ->
import UIKit
import AVFoundation
class Scanner_ViewController: UIViewController, AVCaptureMetadataOutputObjectsDelegate, ScannerDelegate
{
private var scanner: Scanner?
override func viewDidLoad()
{
super.viewDidLoad()
self.scanner = Scanner(withDelegate: self)
guard let scanner = self.scanner else
{
return
}
scanner.requestCaptureSessionStartRunning()
}
override func didReceiveMemoryWarning()
{
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// Mark - AVFoundation delegate methods
public func metadataOutput(_ output: AVCaptureMetadataOutput,
didOutput metadataObjects: [AVMetadataObject],
from connection: AVCaptureConnection)
{
guard let scanner = self.scanner else
{
return
}
scanner.metadataOutput(output,
didOutput: metadataObjects,
from: connection)
}
// Mark - Scanner delegate methods
func cameraView() -> UIView
{
return self.view
}
func delegateViewController() -> UIViewController
{
return self
}
func scanCompleted(withCode code: String)
{
print(code)
showAlert_Success(withTitle: (code))
}
private func showAlert_Success(withTitle title: String)
{
let alertController = UIAlertController(title: title, message: "Product has been successfully scanned", preferredStyle: .alert)
// programatically segue to the next view controller when the UIAlert pops up
alertController.addAction(UIAlertAction(title:"Get Results", style: .default, handler:{ action in self.performSegue(withIdentifier: "toAnalysisPage", sender: self) }))
present(alertController, animated: true)
}
}
Scanner.Swift (accompanies Scanner_ViewController.swift)->
import Foundation
import UIKit
import AVFoundation
protocol ScannerDelegate: class
{
func cameraView() -> UIView
func delegateViewController() -> UIViewController
func scanCompleted(withCode code: String)
}
class Scanner: NSObject
{
public weak var delegate: ScannerDelegate?
private var captureSession : AVCaptureSession?
init(withDelegate delegate: ScannerDelegate)
{
self.delegate = delegate
super.init()
self.scannerSetup()
}
private func scannerSetup()
{
guard let captureSession = self.createCaptureSession()
else
{
return
}
self.captureSession = captureSession
guard let delegate = self.delegate
else
{
return
}
let cameraView = delegate.cameraView()
let previewLayer = self.createPreviewLayer(withCaptureSession: captureSession,
view: cameraView)
cameraView.layer.addSublayer(previewLayer)
}
private func createCaptureSession() -> AVCaptureSession?
{
do
{
let captureSession = AVCaptureSession()
guard let captureDevice = AVCaptureDevice.default(for: .video) else
{
return nil
}
let deviceInput = try AVCaptureDeviceInput(device: captureDevice)
let metaDataOutput = AVCaptureMetadataOutput()
// add device input
if captureSession.canAddInput(deviceInput) && captureSession.canAddOutput(metaDataOutput)
{
captureSession.addInput(deviceInput)
captureSession.addOutput(metaDataOutput)
guard let delegate = self.delegate,
let viewController = delegate.delegateViewController() as? AVCaptureMetadataOutputObjectsDelegate else
{
return nil
}
metaDataOutput.setMetadataObjectsDelegate(viewController,
queue: DispatchQueue.main)
metaDataOutput.metadataObjectTypes = self.metaObjectTypes()
return captureSession
}
}
catch
{
// handle error
}
return nil
}
private func createPreviewLayer(withCaptureSession captureSession: AVCaptureSession,
view: UIView) -> AVCaptureVideoPreviewLayer
{
let previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
previewLayer.frame = view.layer.bounds
previewLayer.videoGravity = .resizeAspectFill
return previewLayer
}
private func metaObjectTypes() -> [AVMetadataObject.ObjectType]
{
return [.qr,
.code128,
.code39,
.code39Mod43,
.code93,
.ean13,
.ean8,
.interleaved2of5,
.itf14,
.pdf417,
.upce
]
}
public func metadataOutput(_ output: AVCaptureMetadataOutput,
didOutput metadataObjects: [AVMetadataObject],
from connection: AVCaptureConnection)
{
self.requestCaptureSessionStopRunning()
guard let metadataObject = metadataObjects.first,
let readableObject = metadataObject as? AVMetadataMachineReadableCodeObject,
let scannedValue = readableObject.stringValue,
let delegate = self.delegate
else
{
return
}
delegate.scanCompleted(withCode: scannedValue)
}
public func requestCaptureSessionStartRunning()
{
self.toggleCaptureSessionRunningState()
}
public func requestCaptureSessionStopRunning()
{
self.toggleCaptureSessionRunningState()
}
private func toggleCaptureSessionRunningState()
{
guard let captureSession = self.captureSession
else
{
return
}
if !captureSession.isRunning
{
captureSession.startRunning()
}
else
{
captureSession.stopRunning()
}
}
}
Analysis_ViewController.swift (second view controller) ->
Right now, the forKey: has been hard-coded to item ID 8710908501708 because I have no idea how to actually pass camera-scanned ID's into the second View Controller :/
import UIKit
class Analysis_ViewController: UIViewController
{
#IBOutlet weak var productTitle: UILabel!
func getData()
{
let path = Bundle.main.path(forResource:"ProductCatalog", ofType: "plist")
let dict:NSDictionary = NSDictionary(contentsOfFile: path!)!
if (dict.object(forKey: "8710908501708" as Any) != nil)
{
if let levelDict:[String : Any] = dict.object(forKey: "8710908501708" as Any) as? [String : Any]
{
// use a for loop to iterate through all the keys and values in side the "Levels" dictionary
for (key, value) in levelDict
{
// if we find a key named whatever we care about, we can print out the value
if (key == "name")
{
productTitle.text = (value as! String)
}
}
}
}
}
// listing the better options that are safer in comparison to the scanned product image
override func viewDidLoad()
{
super.viewDidLoad()
getData()
}
}
Do you have a variable to hold the scanned ID in your view controllers? If not, you can add var itemID: String? to both Scanner_ViewController and Analysis_ViewController.
Then in your func where you get the scanned code, you can set it to the variable.
func scanCompleted(withCode code: String) {
print(code)
itemID = code // Saves the scanned code to your var
showAlert_Success(withTitle: (code))
}
For passing data to another view controller via segue, you might want to look into this UIViewController method for segues: documentation here. This answer also might help.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "toAnalysisPage" {
if let viewController = segue.destination as? Analysis_ViewController {
viewController.itemID = itemID
}
}
}

Why is my UIViewController not showing up in my popup card?

I wanted to create a pop up for one of my UIViewController and found this repo on GitHub.
It is working fine with my InfoViewController which only has 4 UILabels (I think this might be the problem that it is not showing up when you use reusable cells)
But somehow it is not working with my StructureNavigationListViewController and I do not know why.
I call the didTapCategory method in my MainViewController where the StructureNavigationController should pop up but I only see the dimming view (which is weird cause the tap recognizer and pan gestures are working fine but no content is showing up)
In my MainViewController I set up the popup like before:
#IBAction func didTapCategory(_ sender: UIBarButtonItem) {
let popupContent = StructureNavigationListViewController.create()
let cardpopUp = SBCardPopupViewController(contentViewController: popupContent)
cardpopUp.show(onViewController: self)
}
In my StructureNavigationListViewController I set up the table view and the pop up:
public var popupViewController: SBCardPopupViewController?
public var allowsTapToDismissPopupCard: Bool = true
public var allowsSwipeToDismissPopupCard: Bool = true
static func create() -> UIViewController {
let sb = UIStoryboard(name: "Main", bundle: nil)
let vc = sb.instantiateViewController(withIdentifier: "StructureNavigationListViewController") as! StructureNavigationListViewController
return vc
}
#IBOutlet var tableView: UITableView!
var structures = Variable<[Structure]>([])
public var treeSource: StructureTreeSource?
let disposeBag = DisposeBag()
var depthDictionary : [String : Int] = [:]
public override func viewDidLoad() {
structures.asObservable()
.bind(to:tableView.rx.items) {(tableView, row, structure) in
let cell = tableView.dequeueReusableCell(withIdentifier: "StructureNavigationCell", for: IndexPath(row: row, section: 0)) as! StructureNavigationCell
cell.structureLabel.text = structure.name
cell.spacingViewWidthConstraint.constant = 20 * CGFloat(self.depthDictionary[structure.id]!)
return cell
}.disposed(by:disposeBag)
_ = tableView.rx.modelSelected(Structure.self).subscribe(onNext: { structure in
let storyBoard = UIStoryboard(name:"Main", bundle:nil)
let plansViewCtrl = storyBoard.instantiateViewController(withIdentifier: "PlansViewController2") as! PlansViewController2
self.treeSource?.select(structure)
plansViewCtrl.treeSource = self.treeSource
plansViewCtrl.navigationItem.title = structure.name
self.show(plansViewCtrl, sender: self)
if let mainVC = self.parent as? ProjectOverViewTabController2 {
mainVC.addChildView(viewController: plansViewCtrl, in: mainVC.scrollView)
}
})
showList()
}
func showList() {
if treeSource == nil {
treeSource = StructureTreeSource(projectId:GlobalState.selectedProjectId!)
}
//The following piece of code achieves the correct order of structures and their substructures.
//It is extremely bad designed and rather expensive with lots of structures and should
//therefore be refactored!
if let strctrs = getStructures() {
var sortedStructures : [Structure] = []
while(sortedStructures.count != strctrs.count) {
for strct in strctrs {
if let _ = sortedStructures.index(of: strct) {
continue
} else {
depthDictionary[strct.id] = getDepthOfNode(structure: strct, depth: 1)
if let structures = getStructures() {
if let parent = structures.first(where: {$0.id == strct.parentId}) {
if let index = sortedStructures.index(of: parent) {
sortedStructures.insert(strct, at: index+1)
}
} else {
sortedStructures.insert(strct, at: 0)
}
}
}
}
}
structures.value = sortedStructures
tableView.reloadData()
}
}
func getDepthOfNode(structure: Structure, depth: Int) -> Int {
if(structure.parentId == nil || structure.parentId == "") {
return depth
} else {
if let structures = getStructures() {
if let parent = structures.first(where: {$0.id == structure.parentId}) {
return getDepthOfNode(structure: parent, depth: depth + 1)
}
}
}
return -1
}
private func getStructures() -> Results<Structure>? {
do {
if let projectId = GlobalState.selectedProjectId {
return try Structure.db.by(projectId: projectId)
}
} catch { Log.db.error(error: error) }
return nil
}
}
Lot of code here. Sorry..
Is it because I call the create() method after the viewDidLoad() dequeues the cells?
It's hard to tell what is the problem, since you left no information about where didTapCategory is supposed to be called, but maybe it has something to do with your modelSelected subscription being prematurely released?
Edit:
As posted here: https://stackoverflow.com/a/28896452/11851832 if your custom cell is built with Interface Builder then you should register the Nib, not the class:
tableView.registerNib(UINib(nibName: "CustomCell", bundle: nil), forCellReuseIdentifier: "CustomCellIdentifier")

How do i turn of all other switches in a UITableview except the one i clicked

I have a tableView that has a UISwitch in it I am using notificationCenter to get the row of the UISwitch. How do I now use that info to turn off every other switch except the switch that I clicked?
func creatNotification() {
let switchNotification = Notification.Name("answer")
NotificationCenter.default.addObserver(self,selector: #selector(getSwitchRow),name:switchNotification, object:nil)
}
#objc func getSwitchRow(notification: NSNotification){
rowNumber = notification.object as! Int
print("dataView2, getSwitchRow, rowNumber:", rowNumber)
}
#IBAction func `switch`(_ sender: UISwitch) {
var dataViewObject = DataView2()
dataViewObject.creatNotification()
let switchNotification = Notification.Name("answer")
NotificationCenter.default.post(name: switchNotification, object: rowNumber)
let switchNotification2 = Notification.Name("switch")
NotificationCenter.default.post(name: switchNotification, object: answerSwitch.isOn)
if(answerSwitch.isOn == true){
}
}
you can define an attribute : var previousSwitch: UISwitch?
Then the Selector :
#objc func setOn(sender: UISwitch,indexPath: IndexPath){
if previousSwitch != nil {
previousSwitch?.isOn = false
}
let sw = sender
let isSetOn = sw.isOn
if (isSetOn){
print("on")
}else{
print("off")
}
previousSwitch = sw
}
and in Cell :
sw.addTarget(self, action: #selector(setOn(sender:indexPath:)), for: .valueChanged)
cell?.contentView.addSubview(sw)

Swipe left and right between Core Data with a label

I hope you can help.
There is a label and when any user swipe on it. It will make a call to core data and show values on label. Data will depend upon gesture. If its left/right data will be different on each time. Below is the code that i have written. Please suggest whether its correct or not?
class ViewController: UIViewController {
var helloArray = [Tasks]()
var currentArrayIndex = 0
#IBOutlet weak var textField: UITextField!
#IBOutlet weak var helloLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
let leftSwipe = UISwipeGestureRecognizer(target: self, action: #selector(ViewController.handleSwipes(sender:)))
let rightSwipe = UISwipeGestureRecognizer(target: self, action: #selector(ViewController.handleSwipes(sender:)))
leftSwipe.direction = .left
rightSwipe.direction = .right
view.addGestureRecognizer(leftSwipe)
view.addGestureRecognizer(rightSwipe)
ouputData()
}
#objc func handleSwipes(sender: UISwipeGestureRecognizer) {
if sender.direction == .left {
currentArrayIndex = (currentArrayIndex + 1) % 3
}
}
func ouputData() {
do {
helloArray = try context.fetch(Tasks.fetchRequest())
for each in helloArray {
helloLabel.text = each.name
}
} catch {
}
appDelegate.saveContext()
}
#IBAction func btnPressed(_ sender: Any) {
let infoTasks = Tasks(context: context)
infoTasks.name = textField.text
appDelegate.saveContext()
do {
try context.save()
} catch {
print("Error")
}
textField.text = ""
}
}
I think you need to modify your handleSwipes and outputData function.
You outputData should only fetch all the data from CD in viewDidload.
Once you have the data source you can fetch item from source and populate your helloLabel based on index.
#objc func handleSwipes(sender: UISwipeGestureRecognizer) {
if sender.direction == .left {
currentArrayIndex = (currentArrayIndex + 1) % 3
} else if sender.direction == .right {
currentArrayIndex = (currentArrayIndex - 1) % 3
}
helloLabel.text = helloArray[currentArrayIndex].name
}
And:
func ouputData() {
do {
helloArray = try context.fetch(Tasks.fetchRequest())
} catch {
}
appDelegate.saveContext()
}
Hope it helps.

my VC has no initializers

When I had XCode 6.1 everything worked well. After having XCOde 6.3 I am having problem with delegate methods.
Before:
protocol MainPageLoaderViewControllerDelegate{
func changeCategoryOfSingelTopicViewController(category: Int!)
}
class MainPageLoaderViewController: UIViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate, UIGestureRecognizerDelegate {
var delegate: MainPageLoaderViewControllerDelegate?
var categoryOfSingleTopic: Int! = 0 {
didSet{
delegate?.changeCategoryOfSingelTopicViewController(categoryOfSingleTopic!)
}
}
}
Now, Complier gives me error saying that MainPageLoaderViewController has no initializers. How should I declare delegate var?
The all code:
import UIKit
protocol MainPageLoaderViewControllerDelegate{
func changeCategoryOfSingelTopicViewController(category: Int!)
}
class MainPageLoaderViewController: UIViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate, UIGestureRecognizerDelegate {
var delegate: MainPageLoaderViewControllerDelegate?
var categoryOfSingleTopic: Int! = 0{
didSet{
// put to the right column on pageviewcontroller
movePageContentToViewControllerAtIndex(1)
println("here is single topic \(categoryOfSingleTopic)")
delegate?.changeCategoryOfSingelTopicViewController(categoryOfSingleTopic!)
changeADVControleItemsName(categoryOfSingleTopic)
}
}
let gestureRecognizerOfMainPageController: UIPanGestureRecognizer!
#IBOutlet var scrollContentView: UIView!
#IBOutlet var segmentControl: ADVSegmentedControl!
let transtionManger = TransitionManger()
var pageIndexTest: Int!
#IBOutlet var scrollView: UIScrollView!
var mainPageViewController : UIPageViewController!
var tableViewControllers = [UITableViewController]()
var newsLenta: UITableViewController!
var mainPage: SingleTopicTableViewController!
var onlinetranslation: UITableViewController!
var identifiers:NSArray = ["MainPageContentViewController", "MainPageTableViewController", "SingleTopicTableViewController"]
var pageContentViewController: UITableViewController!
override func viewDidLoad() {
super.viewDidLoad()
// self.scrollView.scrollEnabled = false
self.transtionManger.sourceViewController = self
CommonFunctions.setBackgroundImageToNavBar(self.navigationItem)
println("view did load of page controller loaded")
segmentControl.thumbColor = Design.setColorGrey20()
createArrayOfControllers()
resetToMainPage(1)
segmentControl.items = ["Лента", "Главная", "Онлайн"]
segmentControl.font = UIFont(name: "Avenir-Black", size: 12)
segmentControl.borderColor = UIColor(white: 1.0, alpha: 0.3)
segmentControl.selectedIndex = 1
segmentControl.selectedLabelColor = UIColor.whiteColor()
setGestureRecognizerToTableView()
self.transtionManger.segmentToSetInteraction = segmentControl
scrollContentView.addGestureRecognizer(transtionManger.exitPanGesture)
segmentControl.addTarget(self, action: "selectPageIndexBySegmentControl", forControlEvents: UIControlEvents.ValueChanged)
}
func changeADVControleItemsName(category:Int){
switch category{
case 0: segmentControl.items[1] = "Главная" // main
case 100: segmentControl.items[1] = "Избранные" // saved
case 1: segmentControl.items[1] = "Экономика" // economy
case 2: segmentControl.items[1] = "Политика" // politics
case 4: segmentControl.items[1] = "Общество" // community
case 5: segmentControl.items[1] = "Спорт" // sport
case 6: segmentControl.items[1] = "Культура" // culture
case 8: segmentControl.items[1] = "Проишествия" // events
case 10: segmentControl.items[1] = "Авто" // auto
case 11: segmentControl.items[1] = "Фото" // photo
case 12: segmentControl.items[1] = "Видео" // video
default: break
}
}
func changeCategoryOfSingelTopicViewController(category: Int!){
}
func setGestureRecognizerToTableView(){
self.transtionManger.tableViewFromSourceView = self.viewControllerAtIndex(segmentControl.selectedIndex) // we do it in order to disable table view until the menu is opened
}
func movePageContentToViewControllerAtIndex(index: Int){
switch index {
case 0:
println("zero index were selected")
pageContentViewController = self.viewControllerAtIndex(index)
mainPageViewController.setViewControllers([pageContentViewController!], direction: UIPageViewControllerNavigationDirection.Forward , animated: true, completion: nil)
setGestureRecognizerToTableView()
setExitPaGestureAtViewControllerWithIndex(segmentControl.selectedIndex)
// segmentControl.selectedIndex = index
case 1:
println("first element were selected")
pageContentViewController = self.viewControllerAtIndex(index)
mainPageViewController.setViewControllers([pageContentViewController!], direction: UIPageViewControllerNavigationDirection.Forward, animated: true, completion: nil)
setGestureRecognizerToTableView()
setExitPaGestureAtViewControllerWithIndex(segmentControl.selectedIndex)
segmentControl.selectedIndex = index
case 2:
println("second element were selected")
pageContentViewController = self.viewControllerAtIndex(index)
mainPageViewController.setViewControllers([pageContentViewController!], direction: UIPageViewControllerNavigationDirection.Forward, animated: true, completion: nil)
setGestureRecognizerToTableView()
setExitPaGestureAtViewControllerWithIndex(segmentControl.selectedIndex)
default: break
}
}
func selectPageIndexBySegmentControl(){
switch segmentControl.selectedIndex {
case 0:
movePageContentToViewControllerAtIndex(0)
case 1:
movePageContentToViewControllerAtIndex(1)
case 2:
movePageContentToViewControllerAtIndex(2)
default:
break
}
}
#IBAction func showOrCloseMenu(sender: AnyObject) {
if transtionManger.isMenuVisible == true {
println("it is true")
transtionManger.isMenuVisible = false
transtionManger.menuViewController.performSegueWithIdentifier("dismisMenu", sender: nil)
}
else{
println("it is false")
performSegueWithIdentifier("showMenu", sender: nil)
}
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let menu = segue.destinationViewController as! SideBarMenuTableViewController
if transtionManger.isMenuVisible == true {
transtionManger.presentingP = false
}
menu.transitioningDelegate = self.transtionManger
// add in order to set menuviewcontroller in transformerManager
self.transtionManger.menuViewController = menu
}
#IBAction func unwindSegueToMainScreen(segue:UIStoryboardSegue) {
// bug? exit segue doesn't dismiss so we do it manually...
self.dismissViewControllerAnimated(true, completion: nil)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func createArrayOfControllers(){
newsLenta = self.storyboard?.instantiateViewControllerWithIdentifier("NewsLentaTableViewController")as! NewsLentaTableViewController
mainPage = self.storyboard?.instantiateViewControllerWithIdentifier("SingleTopicTableViewController") as! SingleTopicTableViewController
self.delegate = mainPage
onlinetranslation = self.storyboard?.instantiateViewControllerWithIdentifier("MainPageTableViewController") as! MainPageTableViewController
tableViewControllers = [newsLenta, mainPage, onlinetranslation]
}
func resetToMainPage(index: Int!) {
/* Getting the page View controller */
mainPageViewController = self.storyboard?.instantiateViewControllerWithIdentifier("MainPageViewController") as! UIPageViewController
self.mainPageViewController.dataSource = self
self.mainPageViewController.delegate = self
pageContentViewController = self.viewControllerAtIndex(index)
// pageContentViewController.view.addGestureRecognizer(transtionManger.exitPanGesture3)
// self.transtionManger.sourceViewController = pageContentViewController // adding swipe to the pageContentViewControlle in order to close menu
//self.transtionManger.sourceViewController = mainPageViewController
self.mainPageViewController.setViewControllers([pageContentViewController!], direction: UIPageViewControllerNavigationDirection.Forward, animated: true, completion: nil)
self.mainPageViewController.view.frame = CGRectMake(0, 102, self.view.frame.width, self.view.frame.height)
self.addChildViewController(mainPageViewController)
self.view.addSubview(mainPageViewController.view)
self.mainPageViewController.didMoveToParentViewController(self)
}
func viewControllerAtIndex(index : Int) -> UITableViewController? {
if index > 2 || index < 0 {
return nil
}
return tableViewControllers[index]
}
func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
var index:Int!
if viewController.isKindOfClass(NewsLentaTableViewController) {
index = (viewController as! NewsLentaTableViewController).pageIndex
}else if viewController.isKindOfClass(MainPageTableViewController) {
index = (viewController as! MainPageTableViewController).pageIndex
}else if viewController.isKindOfClass(SingleTopicTableViewController) {
index = (viewController as! SingleTopicTableViewController).pageIndex
}else {return nil}
if index == 2 {
return nil
}
index = index + 1
return self.viewControllerAtIndex(index)
}
func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
var index:Int!
if viewController.isKindOfClass(NewsLentaTableViewController) {
index = (viewController as! NewsLentaTableViewController).pageIndex
}else if viewController.isKindOfClass(MainPageTableViewController) {
index = (viewController as! MainPageTableViewController).pageIndex
}else if viewController.isKindOfClass(SingleTopicTableViewController) {
index = (viewController as! SingleTopicTableViewController).pageIndex
}else {return nil}
if index == 0 {
return nil
}
index = index - 1
return self.viewControllerAtIndex(index)
}
func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int
{
return 2
}
func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int
{
return thePageIndex
}
func pageViewController(pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [AnyObject], transitionCompleted completed: Bool) {
if completed && finished {
var index = previousViewControllers.startIndex
segmentControl.selectedIndex = thePageIndex
// add exit3 gesture recognizer to the current index
setExitPaGestureAtViewControllerWithIndex(segmentControl.selectedIndex)
setGestureRecognizerToTableView()
//selectedLabelFrame
//setContentOffSetOfUIScrollView()
}
}
override func viewDidAppear(animated: Bool) {
//setContentOffSetOfUIScrollView() // in the first load of view you need to set segment scroll
setExitPaGestureAtViewControllerWithIndex(segmentControl.selectedIndex)
}
func setExitPaGestureAtViewControllerWithIndex(index: Int!){
self.viewControllerAtIndex(index)?.tableView.addGestureRecognizer(transtionManger.exitPanGesture3)
}
func setContentOffSetOfUIScrollView(){
println(segmentControl.items.count - 1)
if thePageIndex == 0 {
scrollView.setContentOffset(CGPointMake(0, 0), animated: true)
return
}else if thePageIndex == segmentControl.items.count - 1{
if selectedLabelFrame.frame.origin.x + selectedLabelFrame.frame.width > self.view.frame.width {
var sum = selectedLabelFrame.frame.origin.x + selectedLabelFrame.frame.width
var scroll = sum - self.view.frame.width
scrollView.setContentOffset(CGPointMake(scroll, 0), animated: true)
return
}else {
var sum = selectedLabelFrame.frame.origin.x + selectedLabelFrame.frame.width
var scroll = self.view.frame.width - sum
scrollView.setContentOffset(CGPointMake(scroll, 0), animated: true)
return
}
}
if selectedLabelFrame.frame.origin.x + selectedLabelFrame.frame.width > self.view.frame.width / 2 {
println(selectedLabelFrame.frame.origin.x)
println(selectedLabelFrame.frame.width)
println(self.view.frame.width)
println("more")
if thePageIndex == 1 {
var averageWidth = selectedLabelFrame.frame.width/2
var sum = selectedLabelFrame.frame.origin.x + averageWidth
var scroll = sum - self.view.frame.width/2
println("scroll is \(scroll)")
scrollView.setContentOffset(CGPointMake(scroll, 0), animated: true)
}
}else{
println(selectedLabelFrame.frame.origin.x)
println(selectedLabelFrame.frame.width)
println(self.view.frame.width)
println("less")
if thePageIndex == 1 {
var averageWidth = selectedLabelFrame.frame.width/2
var sum = selectedLabelFrame.frame.origin.x + averageWidth
var scroll = self.view.frame.width/2 - sum
println("scroll is \(scroll)")
scrollView.setContentOffset(CGPointMake(scroll, 0), animated: true)
}
}
}
}
In Swift, all instance variables have to be initialized at init (see the documentation) . Before you added the delegate, your superview's initializer was being called (because you hadn't overridden it) and you hadn't added any new instance variables, so this was fine. Now, however, your superview's implementation is being called but it is not initializing your variable, which is a compiler error.
Two options:
1) just initialize in your declaration: var delegate: MainPageLoaderViewControllerDelegate? = nil
2) override init and initialize your variable there.
I solved the problem but still dont understand how it is related to the problem "VC has not initilizers"
I changed the code:
var gestureRecognizerOfMainPageController: UIPanGestureRecognizer!
to
var gestureRecognizerOfMainPageController: UIPanGestureRecognizer?
You need to have a setter method for your variable
var delegate: MainPageLoaderViewControllerDelegate? {
didSet {
// Do whatever changes you wish to do
}
}
or have an init which instantiates the variable with an initial value, or initialize the variable with a value where you declare it.