my VC has no initializers - swift

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.

Related

UIAlertController Already presenting (null)

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)

IGListKitSections doesn't get deallocated

I have a problem with IGListKit sections deallocating. Trying to debug the issue with Xcode memory graph.
My setup is AuthController -> AuthViewModel -> AuthSocialSectionController -> AuthSocialViewModel and some other sections.
AuthController gets presented from several parts of the app if user is not logged in. When I tap close, AuthViewModel and AuthController gets deallocated, but it's underlying sections does not. Memory graph shows nothing leaked in this case, but deinit methods doesn't get called.
But when I'm trying to authorize with social account (successfully) and then look at the memory graph, it shows that sections, that doesn't get deallocated like this:
In this case AuthViewModel doesn't get deallocated either, but after some time it does, but it can happen or not.
I checked every closure and delegate for weak reference, but still no luck.
My code, that I think makes most sense:
class AuthViewController: UIViewController {
fileprivate let collectionView: UICollectionView = UICollectionView(frame: .zero,
collectionViewLayout: UICollectionViewFlowLayout())
lazy var adapter: ListAdapter
= ListAdapter(updater: ListAdapterUpdater(), viewController: self, workingRangeSize: 0)
fileprivate lazy var previewProxy: SJListPreviewProxy = {
SJListPreviewProxy(adapter: adapter)
}()
fileprivate let viewModel: AuthViewModel
fileprivate let disposeBag = DisposeBag()
init(with viewModel: AuthViewModel) {
self.viewModel = viewModel
super.init(nibName: nil, bundle: nil)
hidesBottomBarWhenPushed = true
setupObservers()
}
private func setupObservers() {
NotificationCenter.default.rx.notification(.SJAProfileDidAutoLogin)
.subscribe(
onNext: { [weak self] _ in
self?.viewModel.didSuccessConfirmationEmail()
self?.viewModel.recoverPasswordSuccess()
})
.disposed(by: disposeBag)
}
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: - View Controller Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
setup()
}
// MARK: - Private
#objc private func close() {
dismiss(animated: true, completion: nil)
}
/// Метод настройки экрана
private func setup() {
if isForceTouchEnabled() {
registerForPreviewing(with: previewProxy, sourceView: collectionView)
}
view.backgroundColor = AppColor.instance.gray
title = viewModel.screenName
let item = UIBarButtonItem(image: #imageLiteral(resourceName: "close.pdf"), style: .plain, target: self, action: #selector(AuthViewController.close))
item.accessibilityIdentifier = "auth_close_btn"
asViewController.navigationItem.leftBarButtonItem = item
navigationItem.titleView = UIImageView(image: #imageLiteral(resourceName: "logo_superjob.pdf"))
collectionViewSetup()
}
// Настройка collectionView
private func collectionViewSetup() {
collectionView.keyboardDismissMode = .onDrag
collectionView.backgroundColor = AppColor.instance.gray
view.addSubview(collectionView)
adapter.collectionView = collectionView
adapter.dataSource = self
collectionView.snp.remakeConstraints { make in
make.edges.equalToSuperview()
}
}
}
// MARK: - DataSource CollectionView
extension AuthViewController: ListAdapterDataSource {
func objects(for listAdapter: ListAdapter) -> [ListDiffable] {
return viewModel.sections(for: listAdapter)
}
func listAdapter(_: ListAdapter, sectionControllerFor object: Any) -> ListSectionController {
return viewModel.createListSectionController(for: object)
}
func emptyView(for _: ListAdapter) -> UIView? {
return nil
}
}
// MARK: - AuthViewModelDelegate
extension AuthViewController: AuthViewModelDelegate {
func hideAuth(authSuccessBlock: AuthSuccessAction?) {
dismiss(animated: true, completion: {
authSuccessBlock?()
})
}
func reload(animated: Bool, completion: ((Bool) -> Void)? = nil) {
adapter.performUpdates(animated: animated, completion: completion)
}
func showErrorPopover(with item: CommonAlertPopoverController.Item,
and anchors: (sourceView: UIView, sourceRect: CGRect)) {
let popover = CommonAlertPopoverController(with: item,
preferredContentWidth: view.size.width - 32.0,
sourceView: anchors.sourceView,
sourceRect: anchors.sourceRect,
arrowDirection: .up)
present(popover, animated: true, completion: nil)
}
}
class AuthViewModel {
fileprivate let assembler: AuthSectionsAssembler
fileprivate let router: AuthRouter
fileprivate let profileFacade: SJAProfileFacade
fileprivate let api3ProfileFacade: API3ProfileFacade
fileprivate let analytics: AnalyticsProtocol
fileprivate var sections: [Section] = []
weak var authDelegate: AuthDelegate?
weak var vmDelegate: AuthViewModelDelegate?
var authSuccessBlock: AuthSuccessAction?
private lazy var socialSection: AuthSocialSectionViewModel = { [unowned self] in
self.assembler.socialSection(delegate: self)
}()
init(assembler: AuthSectionsAssembler,
router: AuthRouter,
profileFacade: SJAProfileFacade,
api3ProfileFacade: API3ProfileFacade,
analytics: AnalyticsProtocol,
delegate: AuthDelegate? = nil,
purpose: Purpose) {
self.purpose = purpose
authDelegate = delegate
self.assembler = assembler
self.router = router
self.profileFacade = profileFacade
self.api3ProfileFacade = api3ProfileFacade
self.analytics = analytics
sections = displaySections()
}
private func authDisplaySections() -> [Section] {
let sections: [Section?] = [vacancySection,
authHeaderSection,
socialSection,
authLoginPasswordSection,
signInButtonSection,
switchToSignUpButtonSection,
recoverPasswordSection]
return sections.compactMap { $0 }
}
}
class AuthSocialSectionController: SJListSectionController, SJUpdateCellsLayoutProtocol {
fileprivate let viewModel: AuthSocialSectionViewModel
init(viewModel: AuthSocialSectionViewModel) {
self.viewModel = viewModel
super.init()
minimumInteritemSpacing = 4
viewModel.vmDelegate = self
}
override func cellType(at _: Int) -> UICollectionViewCell.Type {
return AuthSocialCell.self
}
override func cellInitializationType(at _: Int) -> SJListSectionCellInitializationType {
return .code
}
override func configureCell(_ cell: UICollectionViewCell, at index: Int) {
guard let itemCell = cell as? AuthSocialCell else {
return
}
let item = viewModel.item(at: index)
itemCell.imageView.image = item.image
}
override func separationStyle(at _: Int) -> SJCollectionViewCellSeparatorStyle {
return .none
}
}
extension AuthSocialSectionController {
override func numberOfItems() -> Int {
return viewModel.numberOfItems
}
override func didSelectItem(at index: Int) {
viewModel.didSelectItem(at: index)
}
}
// MARK: - AuthSocialSectionViewModelDelegate
extension AuthSocialSectionController: AuthSocialSectionViewModelDelegate {
func sourceViewController() -> UIViewController {
return viewController ?? UIViewController()
}
}
protocol AuthSocialSectionDelegate: class {
func successfullyAuthorized(type: SJASocialAuthorizationType)
func showError(with error: Error)
}
protocol AuthSocialSectionViewModelDelegate: SJListSectionControllerOperationsProtocol, ViewControllerProtocol {
func sourceViewController() -> UIViewController
}
class AuthSocialSectionViewModel: NSObject {
struct Item {
let image: UIImage
let type: SJASocialAuthorizationType
}
weak var delegate: AuthSocialSectionDelegate?
weak var vmDelegate: AuthSocialSectionViewModelDelegate?
fileprivate var items: [Item]
fileprivate let api3ProfileFacade: API3ProfileFacade
fileprivate let analyticsFacade: SJAAnalyticsFacade
fileprivate var socialButtonsDisposeBag = DisposeBag()
init(api3ProfileFacade: API3ProfileFacade,
analyticsFacade: SJAAnalyticsFacade) {
self.api3ProfileFacade = api3ProfileFacade
self.analyticsFacade = analyticsFacade
items = [
Item(image: #imageLiteral(resourceName: "ok_icon.pdf"), type: .OK),
Item(image: #imageLiteral(resourceName: "vk_icon.pdf"), type: .VK),
Item(image: #imageLiteral(resourceName: "facebook_icon.pdf"), type: .facebook),
Item(image: #imageLiteral(resourceName: "mail_icon.pdf"), type: .mail),
Item(image: #imageLiteral(resourceName: "google_icon.pdf"), type: .google),
Item(image: #imageLiteral(resourceName: "yandex_icon.pdf"), type: .yandex)
]
if analyticsFacade.isHHAuthAvailable() {
items.append(Item(image: #imageLiteral(resourceName: "hh_icon"), type: .HH))
}
}
// MARK: - actions
func didSelectItem(at index: Int) {
guard let vc = vmDelegate?.sourceViewController() else {
return
}
let itemType: SJASocialAuthorizationType = items[index].type
socialButtonsDisposeBag = DisposeBag()
api3ProfileFacade.authorize(with: itemType, sourceViewController: vc)
.subscribe(
onNext: { [weak self] _ in
self?.delegate?.successfullyAuthorized(type: itemType)
},
onError: { [weak self] error in
if case let .detailed(errorModel)? = error as? ApplicantError {
self?.vmDelegate?.asViewController.showError(with: errorModel.errors.first?.detail ?? "")
} else {
self?.vmDelegate?.asViewController.showError(with: "Неизвестная ошибка")
}
})
.disposed(by: socialButtonsDisposeBag)
}
}
// MARK: - DataSource
extension AuthSocialSectionViewModel {
var numberOfItems: Int {
return items.count
}
func item(at index: Int) -> Item {
return items[index]
}
}
// MARK: - ListDiffable
extension AuthSocialSectionViewModel: ListDiffable {
func diffIdentifier() -> NSObjectProtocol {
return ObjectIdentifier(self).hashValue as NSObjectProtocol
}
func isEqual(toDiffableObject object: ListDiffable?) -> Bool {
return object is AuthSocialSectionViewModel
}
}
Where assembler is responsible for creating everyting, for example AuthSocialSection:
func socialSection(delegate: AuthSocialSectionDelegate?) -> AuthSocialSectionViewModel {
let vm = AuthSocialSectionViewModel(api3ProfileFacade: api3ProfileFacade,
analyticsFacade: analyticsFacade)
vm.delegate = delegate
return vm
}
How can I properly debug this issue? Any advice or help is really appreciated
Found an issue in AuthSocialSectionController. Somehow passing viewController from IGList context through delegates caused memory issues. When I commented out the viewModel.vmDelegate = self the issue was gone.
That explains why the AuthViewModel was deallocating properly when I hit close button without attempting to authorize. Only when I hit authorize, that viewController property was called.
Thanks for help #vpoltave
This lines from your AuthViewController can this cause leaks?
// adapter has viewController: self
lazy var adapter: ListAdapter
= ListAdapter(updater: ListAdapterUpdater(), viewController: self, workingRangeSize: 0)
fileprivate lazy var previewProxy: SJListPreviewProxy = {
// capture self.adapter ?
SJListPreviewProxy(adapter: adapter)
}()
I'm not sure, but at least you can try :)
UPDATE
I was wondering about this lazy closures and self inside, it won't create retain cycle because lazy initialization are #nonescaping.

How to change value of label from another UICollectionViewCell

What if the UILabel is in class A and the didTapRightutton that will animate it is in class B?
the percentDiscountLabel is in RandomizeDealsCollectionViewCell. This should animate into fade appear if I tap didTapRightutton which is in a different VC called RandomizeDealsViewController
How do I call the function that is inside RandomizeDealsCollectionViewCell to animate the percentDiscountLabel? Is there other way to do this?
class RandomizeDealsCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var percentDiscountLabel: UILabel!
func animatePercentDiscountLabel(deals: String) {
self.percentDiscountLabel.alpha = 0.6
self.percentDiscountLabel.isHidden = false
UIView.animate(withDuration: 0.6, delay: 0, options: .curveEaseInOut, animations: {
self.percentDiscountLabel.alpha = 1.0
}) { (isCompleted) in
}
percentDiscountLabel.text = deals
}
}
class RandomizeDealsViewController: UIViewController {
private var centeredCollectionViewFlowLayout: CenteredCollectionViewFlowLayout!
#IBAction func didTapRightButton(_ sender: Any) {
guard let indexCard = centeredCollectionViewFlowLayout.currentCenteredPage else { return }
if (indexCard > 0) {
centeredCollectionViewFlowLayout.scrollToPage(index: indexCard + 1, animated: true)
// should call the animation function here
}
}
}
If indexCard is indexPath of collectionViewCell which you want to animate,
You can call your cell like ->
in RandomizeDealsViewController
#IBAction func didTapRightButton(_ sender: Any) {
guard let indexCard = centeredCollectionViewFlowLayout.currentCenteredPage else { return }
if (indexCard > 0) {
centeredCollectionViewFlowLayout.scrollToPage(index: indexCard + 1, animated: true)
// should call the animation function here
let cell = collectionView!.cellForItemAtIndexPath(indexCard) as? RandomizeDealsCollectionViewCell
cell?.animatePercentDiscountLabel(deals: "deals")
}
}

How to make Images clickable in a UIPageViewController?

I am creating a UIPageController which swipes 4 pages. In each page there is an image from the array I created. Now I want to make each image from the swipe view clickable to present a new specific page. Each image from the swipe view leads to a different 10 levels (buttons) page.
the project file is here:
http://s000.tinyupload.com/?file_id=90198426971136689376
This is my code in ViewController:
private var pageViewController: UIPageViewController?
private let contentImages = ["Pack_1.png",
"Pack_2.png",
"Pack_3.png",
"nature_pic_4.png"];
override func viewDidLoad() {
super.viewDidLoad()
createPageViewController()
setupPageControl()
}
private func createPageViewController() {
let pageController = self.storyboard!.instantiateViewControllerWithIdentifier("PageController") as! UIPageViewController
pageController.dataSource = self
if contentImages.count > 0 {
let firstController = getItemController(0)!
let startingViewControllers: NSArray = [firstController]
pageController.setViewControllers(startingViewControllers as? [UIViewController], direction: UIPageViewControllerNavigationDirection.Forward, animated: false, completion: nil)
}
pageViewController = pageController
addChildViewController(pageViewController!)
self.view.addSubview(pageViewController!.view)
pageViewController!.didMoveToParentViewController(self)
}
private func setupPageControl() {
let appearance = UIPageControl.appearance()
appearance.pageIndicatorTintColor = UIColor.grayColor()
appearance.currentPageIndicatorTintColor = UIColor.whiteColor()
appearance.backgroundColor = UIColor.darkGrayColor()
}
func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
let itemController = viewController as! PageItemController
if itemController.itemIndex > 0 {
return getItemController(itemController.itemIndex-1)
}
return nil
}
func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
let itemController = viewController as! PageItemController
if itemController.itemIndex+1 < contentImages.count {
return getItemController(itemController.itemIndex+1)
}
return nil
}
private func getItemController(itemIndex: Int) -> PageItemController? {
if itemIndex < contentImages.count {
let pageItemController = self.storyboard!.instantiateViewControllerWithIdentifier("ItemController") as! PageItemController
pageItemController.itemIndex = itemIndex
pageItemController.imageName = contentImages[itemIndex]
return pageItemController
}
return nil
}
}
and this code is in my pageItemController:
var itemIndex: Int = 0
var imageName: String = "" {
didSet {
if let imageView = contentImageView {
imageView.image = UIImage(named: imageName)
}
}
}
#IBOutlet var contentImageView: UIImageView?
override func viewDidLoad() {
super.viewDidLoad()
contentImageView!.image = UIImage(named: imageName)
self.view.backgroundColor = UIColor (red: 100, green: 100, blue: 100, alpha: 0)
}
}
As per this version of the quesion:
"I'm creating a UIPageControllerView that shows 4 images. is there any way to make this images clickable? each image should present a dedicate page. this is my code in viewController:"
SOLUTION:
Use UIGestureRecognizer.
1) Click on your Main.Storyboard.
2) Select UIGestureRecognizer.
3) Drag it on your Image of choice.
3.5) Use Cmd+Alt+Enter to open the Assistant Editor
4) Create an IBAction by Ctrl-dragging from your UITapGestureRecogniser to the Assistant Editor.
5) Put this code in your ViewController.
class ViewController {
let itemIndex: Int!
func pageViewController(pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool)
{
if (!completed)
{
return
}
self.pageControl.currentPageIndex = pageViewController.viewControllers!.first!.view.tag //Page Index
self.itemIndex = self.pageControl.currentPageIndex
}
#IBAction func presentDedicatedPage(sender: UIImageView) {
//pseudo-code here, for example:
switch self.itemIndex {
case 0:
// present these 10 levels
break
case 1:
//present these other 10 levels
break
case 2:
//present these other 10 levels
break
case 3:
//present these other 10 levels
break
}
}
On your ItemPageController:
var itemIndex:Int?
var imageName:String?
Add UITapGesture To ImageView.
override func viewDidLoad() {
super.viewDidLoad()
let tapGestureRecognizer = UITapGestureRecognizer(target:self, action:Selector("imageTapped:"))
targetImageView.userInteractionEnabled = true
targetImageView.addGestureRecognizer(tapGestureRecognizer)
targetImageView.image = UIImage(named: imageName!)
}
On its triggered method:
func imageTapped(img: AnyObject)
{
print(imageName)
print(itemIndex)
//Using a switch statement
let targetImageIndex = itemIndex! as Int
switch (targetImageIndex) {
case 0:
print("case 0")
break;
case 1:
print("case 1")
break;
case 2:
print("case 2")
break;
default:
break;
}
}

Changing height of a sidemenu

This is my actual view
I want that the sidemenu begins at the first grey line and not on the top of the view. I would be glad if I could get an answer how i can set the height of it but i am also happy if someone could suggest me a workaround like hiding it behind the searchbar and segmented control. Thank you in advance.
This is my ENSideMenu.swift implementation
import UIKit
#objc public protocol ENSideMenuDelegate {
optional func sideMenuWillOpen()
optional func sideMenuWillClose()
optional func sideMenuDidOpen()
optional func sideMenuDidClose()
optional func sideMenuShouldOpenSideMenu () -> Bool
}
#objc public protocol ENSideMenuProtocol {
var sideMenu : ENSideMenu? { get }
func setContentViewController(contentViewController: UIViewController)
}
public enum ENSideMenuAnimation : Int {
case None
case Default
}
/**
The position of the side view on the screen.
- Left: Left side of the screen
- Right: Right side of the screen
*/
public enum ENSideMenuPosition : Int {
case Left
case Right
}
public extension UIViewController {
/**
Changes current state of side menu view.
*/
public func toggleSideMenuView () {
sideMenuController()?.sideMenu?.toggleMenu()
}
/**
Hides the side menu view.
*/
public func hideSideMenuView () {
sideMenuController()?.sideMenu?.hideSideMenu()
}
/**
Shows the side menu view.
*/
public func showSideMenuView () {
sideMenuController()?.sideMenu?.showSideMenu()
}
/**
Returns a Boolean value indicating whether the side menu is showed.
:returns: BOOL value
*/
public func isSideMenuOpen () -> Bool {
let sieMenuOpen = self.sideMenuController()?.sideMenu?.isMenuOpen
return sieMenuOpen!
}
/**
* You must call this method from viewDidLayoutSubviews in your content view controlers so it fixes size and position of the side menu when the screen
* rotates.
* A convenient way to do it might be creating a subclass of UIViewController that does precisely that and then subclassing your view controllers from it.
*/
func fixSideMenuSize() {
if let navController = self.navigationController as? ENSideMenuNavigationController {
navController.sideMenu?.updateFrame()
}
}
/**
Returns a view controller containing a side menu
:returns: A `UIViewController`responding to `ENSideMenuProtocol` protocol
*/
public func sideMenuController () -> ENSideMenuProtocol? {
var iteration : UIViewController? = self.parentViewController
if (iteration == nil) {
return topMostController()
}
repeat {
if (iteration is ENSideMenuProtocol) {
return iteration as? ENSideMenuProtocol
} else if (iteration?.parentViewController != nil && iteration?.parentViewController != iteration) {
iteration = iteration!.parentViewController
} else {
iteration = nil
}
} while (iteration != nil)
return iteration as? ENSideMenuProtocol
}
internal func topMostController () -> ENSideMenuProtocol? {
var topController : UIViewController? = UIApplication.sharedApplication().keyWindow?.rootViewController
if (topController is UITabBarController) {
topController = (topController as! UITabBarController).selectedViewController
}
while (topController?.presentedViewController is ENSideMenuProtocol) {
topController = topController?.presentedViewController
}
return topController as? ENSideMenuProtocol
}
}
public class ENSideMenu : NSObject, UIGestureRecognizerDelegate {
/// The width of the side menu view. The default value is 160.
public var menuWidth : CGFloat = 90.0 {
didSet {
needUpdateApperance = true
updateFrame()
}
}
private var menuPosition:ENSideMenuPosition = .Right
private var blurStyle: UIBlurEffectStyle = .Light
/// A Boolean value indicating whether the bouncing effect is enabled. The default value is TRUE.
public var bouncingEnabled :Bool = true
/// The duration of the slide animation. Used only when `bouncingEnabled` is FALSE.
public var animationDuration = 0.25
private let sideMenuContainerView = UIView()
private(set) var menuViewController : UIViewController!
private var animator : UIDynamicAnimator!
private var sourceView : UIView!
private var needUpdateApperance : Bool = false
/// The delegate of the side menu
public weak var delegate : ENSideMenuDelegate?
private(set) var isMenuOpen : Bool = false
/// A Boolean value indicating whether the left swipe is enabled.
public var allowLeftSwipe : Bool = true
/// A Boolean value indicating whether the right swipe is enabled.
public var allowRightSwipe : Bool = true
public var allowPanGesture : Bool = true
private var panRecognizer : UIPanGestureRecognizer?
/**
Initializes an instance of a `ENSideMenu` object.
:param: sourceView The parent view of the side menu view.
:param: menuPosition The position of the side menu view.
:returns: An initialized `ENSideMenu` object, added to the specified view.
*/
public init(sourceView: UIView, menuPosition: ENSideMenuPosition, blurStyle: UIBlurEffectStyle = .Light) {
super.init()
self.sourceView = sourceView
self.menuPosition = menuPosition
self.blurStyle = blurStyle
self.setupMenuView()
animator = UIDynamicAnimator(referenceView:sourceView)
animator.delegate = self
self.panRecognizer = UIPanGestureRecognizer(target: self, action: "handlePan:")
panRecognizer!.delegate = self
sourceView.addGestureRecognizer(panRecognizer!)
// Add right swipe gesture recognizer
let rightSwipeGestureRecognizer = UISwipeGestureRecognizer(target: self, action: "handleGesture:")
rightSwipeGestureRecognizer.delegate = self
rightSwipeGestureRecognizer.direction = UISwipeGestureRecognizerDirection.Right
// Add left swipe gesture recognizer
let leftSwipeGestureRecognizer = UISwipeGestureRecognizer(target: self, action: "handleGesture:")
leftSwipeGestureRecognizer.delegate = self
leftSwipeGestureRecognizer.direction = UISwipeGestureRecognizerDirection.Left
if (menuPosition == .Left) {
sourceView.addGestureRecognizer(rightSwipeGestureRecognizer)
sideMenuContainerView.addGestureRecognizer(leftSwipeGestureRecognizer)
}
else {
sideMenuContainerView.addGestureRecognizer(rightSwipeGestureRecognizer)
sourceView.addGestureRecognizer(leftSwipeGestureRecognizer)
}
}
/**
Initializes an instance of a `ENSideMenu` object.
:param: sourceView The parent view of the side menu view.
:param: menuViewController A menu view controller object which will be placed in the side menu view.
:param: menuPosition The position of the side menu view.
:returns: An initialized `ENSideMenu` object, added to the specified view, containing the specified menu view controller.
*/
public convenience init(sourceView: UIView, menuViewController: UIViewController, menuPosition: ENSideMenuPosition, blurStyle: UIBlurEffectStyle = .Light) {
self.init(sourceView: sourceView, menuPosition: menuPosition, blurStyle: blurStyle)
self.menuViewController = menuViewController
self.menuViewController.view.frame = sideMenuContainerView.bounds
self.menuViewController.view.autoresizingMask = [.FlexibleHeight, .FlexibleWidth]
sideMenuContainerView.addSubview(self.menuViewController.view)
}
/*
public convenience init(sourceView: UIView, view: UIView, menuPosition: ENSideMenuPosition) {
self.init(sourceView: sourceView, menuPosition: menuPosition)
view.frame = sideMenuContainerView.bounds
view.autoresizingMask = [.FlexibleHeight, .FlexibleWidth]
sideMenuContainerView.addSubview(view)
}
*/
/**
Updates the frame of the side menu view.
*/
func updateFrame() {
var width:CGFloat
var height:CGFloat
(width, height) = adjustFrameDimensions( sourceView.frame.size.width, height: sourceView.frame.size.height)
let menuFrame = CGRectMake(
(menuPosition == .Left) ?
isMenuOpen ? 0 : -menuWidth-1.0 :
isMenuOpen ? width - menuWidth : width+1.0,
sourceView.frame.origin.y,
menuWidth,
height
)
sideMenuContainerView.frame = menuFrame
}
private func adjustFrameDimensions( width: CGFloat, height: CGFloat ) -> (CGFloat,CGFloat) {
if floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_7_1 &&
(UIApplication.sharedApplication().statusBarOrientation == UIInterfaceOrientation.LandscapeRight ||
UIApplication.sharedApplication().statusBarOrientation == UIInterfaceOrientation.LandscapeLeft) {
// iOS 7.1 or lower and landscape mode -> interchange width and height
return (height, width)
}
else {
return (width, height)
}
}
private func setupMenuView() {
// Configure side menu container
updateFrame()
sideMenuContainerView.backgroundColor = UIColor.clearColor()
sideMenuContainerView.clipsToBounds = false
sideMenuContainerView.layer.masksToBounds = false
sideMenuContainerView.layer.shadowOffset = (menuPosition == .Left) ? CGSizeMake(1.0, 1.0) : CGSizeMake(-1.0, -1.0)
sideMenuContainerView.layer.shadowRadius = 1.25
sideMenuContainerView.layer.shadowOpacity = 0.125
sideMenuContainerView.layer.shadowPath = UIBezierPath(rect: sideMenuContainerView.bounds).CGPath
sourceView.addSubview(sideMenuContainerView)
if (NSClassFromString("UIVisualEffectView") != nil) {
// Add blur view
let visualEffectView = UIVisualEffectView(effect: UIBlurEffect(style: blurStyle)) as UIVisualEffectView
visualEffectView.frame = sideMenuContainerView.bounds
visualEffectView.autoresizingMask = [.FlexibleHeight, .FlexibleWidth]
sideMenuContainerView.addSubview(visualEffectView)
}
else {
// TODO: add blur for ios 7
}
}
private func toggleMenu (shouldOpen: Bool) {
if (shouldOpen && delegate?.sideMenuShouldOpenSideMenu?() == false) {
return
}
updateSideMenuApperanceIfNeeded()
isMenuOpen = shouldOpen
var width:CGFloat
var height:CGFloat
(width, height) = adjustFrameDimensions( sourceView.frame.size.width, height: sourceView.frame.size.height)
if (bouncingEnabled) {
animator.removeAllBehaviors()
var gravityDirectionX: CGFloat
var pushMagnitude: CGFloat
var boundaryPointX: CGFloat
var boundaryPointY: CGFloat
if (menuPosition == .Left) {
// Left side menu
gravityDirectionX = (shouldOpen) ? 1 : -1
pushMagnitude = (shouldOpen) ? 20 : -20
boundaryPointX = (shouldOpen) ? menuWidth : -menuWidth-2
boundaryPointY = 20
}
else {
// Right side menu
gravityDirectionX = (shouldOpen) ? -1 : 1
pushMagnitude = (shouldOpen) ? -20 : 20
boundaryPointX = (shouldOpen) ? width-menuWidth : width+menuWidth+2
boundaryPointY = -20
}
let gravityBehavior = UIGravityBehavior(items: [sideMenuContainerView])
gravityBehavior.gravityDirection = CGVectorMake(gravityDirectionX, 0)
animator.addBehavior(gravityBehavior)
let collisionBehavior = UICollisionBehavior(items: [sideMenuContainerView])
collisionBehavior.addBoundaryWithIdentifier("menuBoundary", fromPoint: CGPointMake(boundaryPointX, boundaryPointY),
toPoint: CGPointMake(boundaryPointX, height))
animator.addBehavior(collisionBehavior)
let pushBehavior = UIPushBehavior(items: [sideMenuContainerView], mode: UIPushBehaviorMode.Instantaneous)
pushBehavior.magnitude = pushMagnitude
animator.addBehavior(pushBehavior)
let menuViewBehavior = UIDynamicItemBehavior(items: [sideMenuContainerView])
menuViewBehavior.elasticity = 0.25
animator.addBehavior(menuViewBehavior)
}
else {
var destFrame :CGRect
if (menuPosition == .Left) {
destFrame = CGRectMake((shouldOpen) ? -2.0 : -menuWidth, 0, menuWidth, height)
}
else {
destFrame = CGRectMake((shouldOpen) ? width-menuWidth : width+2.0,
0,
menuWidth,
height)
}
UIView.animateWithDuration(
animationDuration,
animations: { () -> Void in
self.sideMenuContainerView.frame = destFrame
},
completion: { (Bool) -> Void in
if (self.isMenuOpen) {
self.delegate?.sideMenuDidOpen?()
} else {
self.delegate?.sideMenuDidClose?()
}
})
}
if (shouldOpen) {
delegate?.sideMenuWillOpen?()
} else {
delegate?.sideMenuWillClose?()
}
/*let outterView = UIView(frame: CGRectMake(sideMenuContainerView.frame.width, 0,
sourceView.frame.width - sideMenuContainerView.frame.width,
sourceView.frame.height))
outterView.backgroundColor = UIColor.clearColor()
let tapRecognizer = UITapGestureRecognizer(target: self, action: "hideSideMenu")
outterView.addGestureRecognizer(tapRecognizer)
outterView.userInteractionEnabled = false
sourceView.addSubview(outterView)
sourceView.layer.zPosition = 0
outterView.userInteractionEnabled = shouldOpen */
}
public func gestureRecognizerShouldBegin(gestureRecognizer: UIGestureRecognizer) -> Bool {
if gestureRecognizer is UISwipeGestureRecognizer {
let swipeGestureRecognizer = gestureRecognizer as! UISwipeGestureRecognizer
if !self.allowLeftSwipe {
if swipeGestureRecognizer.direction == .Left {
return false
}
}
if !self.allowRightSwipe {
if swipeGestureRecognizer.direction == .Right {
return false
}
}
}
else if gestureRecognizer.isEqual(panRecognizer) {
if allowPanGesture == false {
return false
}
animator.removeAllBehaviors()
let touchPosition = gestureRecognizer.locationOfTouch(0, inView: sourceView)
if menuPosition == .Left {
if isMenuOpen {
if touchPosition.x < menuWidth {
return true
}
}
else {
if touchPosition.x < 25 {
return true
}
}
}
else {
if isMenuOpen {
if touchPosition.x > CGRectGetWidth(sourceView.frame) - menuWidth {
return true
}
}
else {
if touchPosition.x > CGRectGetWidth(sourceView.frame)-25 {
return true
}
}
}
return false
}
return true
}
internal func handleGesture(gesture: UISwipeGestureRecognizer) {
toggleMenu((self.menuPosition == .Right && gesture.direction == .Left)
|| (self.menuPosition == .Left && gesture.direction == .Right))
}
internal func handlePan(recognizer : UIPanGestureRecognizer){
let leftToRight = recognizer.velocityInView(recognizer.view).x > 0
switch recognizer.state {
case .Began:
break
case .Changed:
let translation = recognizer.translationInView(sourceView).x
let xPoint : CGFloat = sideMenuContainerView.center.x + translation + (menuPosition == .Left ? 1 : -1) * menuWidth / 2
if menuPosition == .Left {
if xPoint <= 0 || xPoint > CGRectGetWidth(self.sideMenuContainerView.frame) {
return
}
}else{
if xPoint <= sourceView.frame.size.width - menuWidth || xPoint >= sourceView.frame.size.width
{
return
}
}
sideMenuContainerView.center.x = sideMenuContainerView.center.x + translation
recognizer.setTranslation(CGPointZero, inView: sourceView)
default:
let shouldClose = menuPosition == .Left ? !leftToRight && CGRectGetMaxX(sideMenuContainerView.frame) < menuWidth : leftToRight && CGRectGetMinX(sideMenuContainerView.frame) > (sourceView.frame.size.width - menuWidth)
toggleMenu(!shouldClose)
}
}
private func updateSideMenuApperanceIfNeeded () {
if (needUpdateApperance) {
var frame = sideMenuContainerView.frame
frame.size.width = menuWidth
sideMenuContainerView.frame = frame
sideMenuContainerView.layer.shadowPath = UIBezierPath(rect: sideMenuContainerView.bounds).CGPath
needUpdateApperance = false
}
}
/**
Toggles the state of the side menu.
*/
public func toggleMenu () {
if (isMenuOpen) {
toggleMenu(false)
}
else {
updateSideMenuApperanceIfNeeded()
toggleMenu(true)
}
}
/**
Shows the side menu if the menu is hidden.
*/
public func showSideMenu () {
if (!isMenuOpen) {
toggleMenu(true)
}
}
/**
Hides the side menu if the menu is showed.
*/
public func hideSideMenu () {
if (isMenuOpen) {
toggleMenu(false)
}
}
}
extension ENSideMenu: UIDynamicAnimatorDelegate {
public func dynamicAnimatorDidPause(animator: UIDynamicAnimator) {
if (self.isMenuOpen) {
self.delegate?.sideMenuDidOpen?()
} else {
self.delegate?.sideMenuDidClose?()
}
}
public func dynamicAnimatorWillResume(animator: UIDynamicAnimator) {
print("resume")
}
}
This is my ENSideMenuNavigationController.swift
import UIKit
public class ENSideMenuNavigationController: UINavigationController, ENSideMenuProtocol {
public var sideMenu : ENSideMenu?
public var sideMenuAnimationType : ENSideMenuAnimation = .Default
// MARK: - Life cycle
public override func viewDidLoad() {
super.viewDidLoad()
}
public init( menuViewController: UIViewController, contentViewController: UIViewController?) {
super.init(nibName: nil, bundle: nil)
if (contentViewController != nil) {
self.viewControllers = [contentViewController!]
}
sideMenu = ENSideMenu(sourceView: self.view, menuViewController: menuViewController, menuPosition:.Right)
view.bringSubviewToFront(navigationBar)
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
public override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Navigation
public func setContentViewController(contentViewController: UIViewController) {
self.sideMenu?.toggleMenu()
switch sideMenuAnimationType {
case .None:
self.viewControllers = [contentViewController]
break
default:
contentViewController.navigationItem.hidesBackButton = true
self.setViewControllers([contentViewController], animated: true)
break
}
}
}
This is my MyMenuTableViewController:
import UIKit
class MyMenuTableViewController: UITableViewController {
var selectedMenuItem : Int = 0
override func viewDidLoad() {
super.viewDidLoad()
// Customize apperance of table view
tableView.contentInset = UIEdgeInsetsMake(116.0, 0, 0, 0) //
tableView.separatorStyle = .None
tableView.backgroundColor = UIColor.whiteColor()
tableView.scrollsToTop = false
tableView.allowsSelection = false
// Preserve selection between presentations
self.clearsSelectionOnViewWillAppear = false
tableView.selectRowAtIndexPath(NSIndexPath(forRow: selectedMenuItem, inSection: 0), animated: false, scrollPosition: .Middle)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// Return the number of sections.
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Return the number of rows in the section.
return 1
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("CELL")
if (cell == nil) {
cell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "CELL")
cell!.backgroundColor = UIColor.clearColor()
cell!.textLabel?.textColor = UIColor.darkGrayColor()
let selectedBackgroundView = UIView(frame: CGRectMake(0, 0, cell!.frame.size.width, cell!.frame.size.height))
selectedBackgroundView.backgroundColor = UIColor.grayColor().colorWithAlphaComponent(0.2)
cell!.selectedBackgroundView = selectedBackgroundView
}
//cell!.textLabel?.text = "social Network #\(indexPath.row+1)"
cell!.imageView?.image = UIImage(named: "Facebook")
return cell!
}
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 45.0
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
print("did select row: \(indexPath.row)")
if (indexPath.row == selectedMenuItem) {
return
}
selectedMenuItem = indexPath.row
//Present new view controller
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main",bundle: nil)
var destViewController : UIViewController
switch (indexPath.row) {
case 0:
destViewController = mainStoryboard.instantiateViewControllerWithIdentifier("ViewController1")
break
case 1:
destViewController = mainStoryboard.instantiateViewControllerWithIdentifier("ViewController2")
break
case 2:
destViewController = mainStoryboard.instantiateViewControllerWithIdentifier("ViewController3")
break
default:
destViewController = mainStoryboard.instantiateViewControllerWithIdentifier("ViewController4")
break
}
sideMenuController()?.setContentViewController(destViewController)
}
}
This is my MyNavigationController.swift
import UIKit
class MyNavigationController: ENSideMenuNavigationController, ENSideMenuDelegate {
override func viewDidLoad() {
super.viewDidLoad()
sideMenu = ENSideMenu(sourceView: self.view, menuViewController: MyMenuTableViewController(), menuPosition:.Right)
//sideMenu?.delegate = self //optional
sideMenu?.menuWidth = 90.0 // optional, default is 160
// sideMenu?.bouncingEnabled = false
//sideMenu?.allowPanGesture = false
// make navigation bar showing over side menu
view.bringSubviewToFront(navigationBar)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - ENSideMenu Delegate
func sideMenuWillOpen() {
print("sideMenuWillOpen")
}
func sideMenuWillClose() {
print("sideMenuWillClose")
}
func sideMenuDidClose() {
print("sideMenuDidClose")
}
func sideMenuDidOpen() {
print("sideMenuDidOpen")
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
In your menu tableViewController you should implement
let MinHeight: CGFloat = 100.0
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
let tHeight = tableView.bounds.height
let temp = tHeight/CGFloat(items.count) //Item size on your side menu
return temp > MinHeight ? temp : MinHeight
}