The delegate in this app is not being set - ios

I have an app with two view controllers... ViewController and CollectionViewController. I have filter functions in the viewController that perform color filters on an image loaded into this view controller. The CollectionViewController contains a collection view that acts as a horizontal scrolling menu with cells, that when pressed, are supposed to call the filter functions in ViewController. The storyboard has a show segue with ID "FilterSegue"
I got it to work with Notification Centers but I wanted to try to get it to work with protocols and delegates to learn about the methods. I received some suggestions, but have been unable to get the delegate to set despite numerous attempts.
Here is the code:
ViewController:
import UIKit
class ViewController: UIViewController, FilterDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "FilterSegue" {
let destvc = segue.destinationViewController as! CollectionViewController
destvc.filterDelegate = self
}
}
func onRedFilter() {
// some code
}
func onGreenFilter() {
// some code
}
func onBlueFilter() {
// some code
}
func onUnfiltered() {
// some code
}
}
CollectionViewController:
import UIKit
//Protocols for filter functions called by the filter menu collection view custom cells.
protocol FilterDelegate: class {
func onRedFilter()
func onGreenFilter()
func onBlueFilter()
func onUnfiltered()
}
class CollectionViewController: UIViewController, UICollectionViewDelegateFlowLayout, UICollectionViewDataSource, UICollectionViewDelegate {
let reuseIdentifier = "FilterCell"
var filterDelegate: FilterDelegate? = nil
#IBOutlet var collectionView: UICollectionView!
// Filter labels for custom filter menu cells.
var tableData:[String] = ["Red Filter",
"Green Filter",
"Blue Filter",
"Unfilter",
"New Filter 1",
"New Filter 2"]
// Filter images for custom filter menu cells.
var tableImages: [String] = ["waterfallred.png",
"waterfallgreen.png",
"waterfallblue.png",
"waterfall.png",
"waterfall.png",
"waterfall.png"]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
// Set up collectionView for the filters.
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
layout.itemSize = CGSize(width: 120, height: 80)
collectionView = UICollectionView(frame: self.view.frame, collectionViewLayout: layout)
collectionView!.registerClass(colvwCell.self, forCellWithReuseIdentifier: reuseIdentifier)
collectionView!.backgroundColor = UIColor.whiteColor()
self.view.addSubview(collectionView!)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// Set uo required methods for collection view.
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return tableData.count
}
// Method for custom collection view cell texts and images.
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell: colvwCell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as! colvwCell
cell.backgroundColor = UIColor.grayColor()
cell.lblCell.text = tableData[indexPath.row]
cell.imgCell.image = UIImage(named: tableImages[indexPath.row])
return cell
}
// Method for calling functions upon pressing custom filter menu collection view cells. In this case, the filter functions in the main view controller are called using notifications.
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
print("Cell \(indexPath.row) selected")
guard let filterDelegate = filterDelegate else {
print("Filter delegate wasn't set!")
return
}
switch indexPath.row {
case 0:
filterDelegate.onRedFilter()
case 1:
filterDelegate.onGreenFilter()
case 2:
filterDelegate.onBlueFilter()
case 3:
filterDelegate.onUnfiltered()
default:
print("No available filter.")
}
}
}
The guard statement returns "Filter delegate wasn't set!" on any depressed cell on the collection view menu.

instead of this
protocol FilterDelegate: class {
func onRedFilter()
----
}
try this
protocol FilterDelegate {
func onRedFilter()
----
}

I found the answer...
I realized that the segue wasn't being set because it was trying to instantiate another collection view controller with the segue. I removed the prepareForSegue altogether and the segue from the storyboard.
I have a button called onFilter2 on the main view controller that instantiates the collection view in a container on a press (calls a function showFilterMenu to show the filter menu) and then hides it on a second press (hideFilterMenu). The showFilterMenu function is where the colectionviewcontroller is instantiated. So, I set the delegate in this function. It took one line of code.
Here is the function executed upon pressing the filter button with the correct call of the delegate.
// New onFilter button action that shows/hides the filter collection view scroll bar on main view controller.
#IBAction func onFilter2(sender: UIButton) {
if filterButtonOn == true {
if (sender.selected) {
hideFilterMenu()
editButton.selected = false
sender.selected = false
} else {
showFilterMenu()
sender.selected = true
hideSliderMenu()
editButton.selected = false
//Reset color sliders to initial values.
redHorizontalSlider?.setValue(0, animated: false)
greenHorizontalSlider?.setValue(0.0, animated: false)
blueHorizontalSlider?.setValue(0.0, animated: false)
// Set initial values for color indicators.
redSliderIndicator.text = "0.00"
greenSliderIndicator.text = "0.00"
blueSliderIndicator.text = "0.00"
}
}
}
// Shows the filter menu scrolling bar in container view upon request.
func showFilterMenu() {
let newViewController = self.storyboard?.instantiateViewControllerWithIdentifier("FilterCollectionView") as! CollectionViewController
newViewController.view.translatesAutoresizingMaskIntoConstraints = false
self.toggleOnViewController(newViewController)
self.currentViewController = newViewController
// Set the delegate for the filter functions called by the cells in the collectionViewController filter menu.
// Set the delegate for the filter functions called by the cells in the collectionViewController filter menu.
//Here is the line of code that fixes the problem...
newViewController.filterDelegate = self
filterMenuShowOn = true
}
// Hides filter menu scrolling bar from container view upon request.
func hideFilterMenu() {
currentViewController!.view.alpha = 1
UIView.animateWithDuration(0.5, animations: {
self.currentViewController!.view.alpha = 0
},
completion: { finished in
self.currentViewController!.view.removeFromSuperview()
self.currentViewController!.removeFromParentViewController()
})
}
I want to thank Matt for getting me thinking about what was happening.

Related

How Do I Get Gesture Recognizers to work on base controller when displaying a popup controller? [duplicate]

I have a collection view and when a cell is selected it presents a popover view showing more information about that cell.
I would like to allow the user to click another cell and then have the popover view change to showing that cell's information without having to close the popover. If the user were to click somewhere on the parent view that isn't a cell then the popover should close. But, I would like the user to still be able to scroll the collection view without closing the popover.
How can that be done?
According to Apple :
When a popover is active, interactions with other views are normally disabled until the popover is dismissed. Assigning an array of views to this property allows taps outside of the popover to be handled by the corresponding views.
Then you can use the passthroughViews in the following way :
CollectionViewController
import UIKit
let reuseIdentifier = "Cell"
class CollectionViewController: UICollectionViewController {
var popoverViewController : PopoverViewController?
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
//#warning Incomplete method implementation -- Return the number of sections
return 1
}
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
//#warning Incomplete method implementation -- Return the number of items in the section
return 15
}
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as! CollectionViewCell
cell.labelInfo.text = "Cell \(indexPath.row)"
return cell
}
override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
println("tapped")
if let popover = self.popoverViewController {
var cell = self.collectionView!.cellForItemAtIndexPath(indexPath) as! CollectionViewCell
popover.labelPop.text = cell.labelInfo.text
}
else {
self.popoverViewController = self.storyboard?.instantiateViewControllerWithIdentifier("PopoverViewController") as? PopoverViewController
var cell = self.collectionView!.cellForItemAtIndexPath(indexPath) as! CollectionViewCell
var t = self.popoverViewController!.view
self.popoverViewController!.labelPop.text = cell.labelInfo.text
self.popoverViewController!.modalPresentationStyle = .Popover
var popover = self.popoverViewController!.popoverPresentationController
popover?.passthroughViews = [self.view]
popover?.sourceRect = CGRect(x: 250, y: 500, width: 0, height: 0)
self.popoverViewController!.preferredContentSize = CGSizeMake(250, 419)
popover!.sourceView = self.view
self.presentViewController(self.popoverViewController!, animated: true, completion: nil)
}
}
}
The above code is the CollectionViewController to handle the UICollectionViewController and all its delegates.
CollectionViewCell
class CollectionViewCell: UICollectionViewCell {
#IBOutlet weak var labelInfo: UILabel!
}
The custom cell with just a UILabel inside.
PopoverViewController
class PopoverViewController: UIViewController {
#IBOutlet var labelPop: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
And finally the PopoverViewController to show in form of .Popover.
There are some observations in answer I would like to point out :
I set a reference to the class PopoverViewController to keep it through the life cycle and pass it data when it remains open yet.
The line var t = self.popoverViewController!.view it's necessary because if not the #IBOutlet inside the PopoverViewController was not init until it's presented, there could be other ways to do it.
I present the popover in the middle of the screen to handle the tap in several cell and test it the scroll too, you can display it in any position you want.
In the views to allow when the popover is opened , I set the self.view, but in this way you need to dismiss it for you own, because it never is dismissed when you make taps in the view, you can put any view you want instead.
Any trouble you have with the solution I can share it the project on Github.
I hope this help you
What you are looking for is the passthroughViews property of the popover.
However, if you open the popover as a result of tapping a cell, I don't see how scrolling the collectionView will make sense. Don't you open the popover with the arrow pointing to your cell? Scrolling the view will make the presenting cell to move away...
You can use property of UIViewController 'modalInPopover' to enable touches outside the popover boundary. Just write the line given below in your view controller which you are presenting using popover controller.
self.modalInPopover = false;
where self is kind of UIViewController.
I have attached a screenshot for the same.
In swift the line will remain same
self.modalInPopover = false

didSelect does not work in UICollectionviewCell to select UICollectionViewController

I wrote didSelectItemAtIndexPath func in UICollectionViewCell to select a UICollectionViewController. I wrote the code in two ways but it doesnot work at all. Also, I don't getting an error in there.
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if indexPath.item == 0 {
let layout = UICollectionViewFlowLayout()
let controller1 = DigitalSLRCon(collectionViewLayout: layout)
let nav = UINavigationController()
nav.pushViewController(controller1, animated: true)
// OR
let layout = UICollectionViewFlowLayout()
let controller1 = DigitalSLRCon(collectionViewLayout: layout)
navigationController?.pushViewController(controller1, animated: true)
}
didSelectItemAtIndexpathis a function of UICollectionViewDelegate - you don't implement it in the cell, but in your CollectionView's delegate, so probably the view controller which contains the collection view.
Make the UIViewController that holds the collection view conform to UICollectionViewDelegate protocol
Assign that view controller to collection view's delegate property
Implement the didSelectItemAtIndexpath function in the view controller
Please remove the delegates from the UICollectionViewCell The delegates are not for the cell but the UICollectionView handler
If you want to change some element in the cell
you can override the default variables in the cell like this
override var isSelected: Bool {
didSet {
if isSelected {
//do something
} else {
//not selected
}
}
}
override var isHighlighted: Bool {
didSet {
if isHighlighted {
//do something
} else {
//not selected
}
}
}

UICollectionView some cells are neither visible items nor dequeued cells

I have an issue in my UICollectionView where some cells seem to be neither part of the indexPathForVisibleItems, nor are they taken from the caching queue with dequeueReusableCell. The result is, that some cells don't receive required updates of data during a scroll and show old behavior.
For simplicity, I reduced the project to the neccessary Controllers and a minimized Storyboard. Basically, I've got a NavigationController as EntryPoint that contains the MainViewController, which itself contains a ContainerView with the CollectionViewController.
The NavigationController uses the default edit button to switch between edit and non-edit mode - this should result in an image displayed on the cells while in edit mode. Therefore I implemented setEditing and changed the images hidden property of all visible cells, and additionally i set the images hidden property while dequeuing - assuming that cells are either visible or they will be dequeued in the future.
This works fine while the CollectionView is scrolled from top to bottom. But when I switch back from Edit-Mode to Non-Edit-Mode while scrolled to the bottom and then scroll back to the top, some cells still display the image (more specific: at least the same row, which is the first non-visible row). Somehow I'd assume that the dequeued cells and the visible cells would be complementary parts of the displayed data, which should result in either the images being hidden/unhidden during the setEditing call (which works for the first 4 rows of cells) or being hidden/unhidden during the dequeuing (which works for the last few rows, except the third row in my example)
Code for the CollectionViewController:
import Foundation
import UIKit
import Photos
class CollectionViewController : UICollectionViewController {
fileprivate let CELL_ID = "PicCell"
fileprivate let IMAGE_VIEW_SIZE = 104
var selectedIndex = -1
var itemCount = 28
override func setEditing(_ editing: Bool, animated: Bool) {
super.setEditing(editing, animated: animated)
print(self.isEditing)
collectionView?.allowsMultipleSelection = editing
for indexPath in (collectionView?.indexPathsForSelectedItems)! {
collectionView?.deselectItem(at: indexPath, animated: true)
}
for indexPath in (collectionView?.indexPathsForVisibleItems)! {
let cell = collectionView?.cellForItem(at: indexPath) as? PicCell
if cell != nil {
cell?.editing = editing
}
}
}
override func viewDidLoad() {
self.navigationItem.rightBarButtonItem = self.editButtonItem
collectionView?.reloadData()
}
}
extension CollectionViewController {
override func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return itemCount;
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
print(indexPath.row)
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CELL_ID, for: indexPath) as! PicCell
cell.editing = isEditing
cell.isSelected = false
return cell;
}
}
Code for PicCell
import Foundation
import UIKit
class PicCell : UICollectionViewCell {
#IBOutlet weak var deleteBtn: UIImageView!
var editing:Bool = false{
didSet {
self.deleteBtn.isHidden = !editing
self.deleteBtn.tintColor = UIColor.blue
}
}
override var isSelected: Bool{
didSet {
if isSelected && editing {
self.deleteBtn.tintColor = UIColor.red
} else {
self.deleteBtn.tintColor = UIColor.blue
}
}
}
override func prepareForReuse() {
super.prepareForReuse()
self.editing = false
self.isSelected = false
}
}
Code for MainViewController
import Foundation
import UIKit
class MainViewController: UIViewController {
override func viewDidLoad() {
self.navigationItem.rightBarButtonItem = self.editButtonItem
}
override func setEditing(_ editing: Bool, animated: Bool) {
super.setEditing(editing, animated: animated)
collectionController?.setEditing(editing, animated: animated)
}
var collectionController : CollectionViewController? = nil
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "ShowCollectionView" {
collectionController = segue.destination as! CollectionViewController
}
}
}
as you can see I'm using constant values for the item count in a single section, which shouldn't be a big deal.
I don't know if it matters, but my cells are 104px in width and height, the image is rendered as template and the UIContainerView is 343 in width and 473 in height, i'm testing on an iPhone 7+ simulator with iOS 10.1
If there's anything missing from the storyboard, i might add some screenshots, but i'm unsure what to post exactly
Thanks in advance for your help and kind regards
Christian
Edit: just to be clear with my question: I'm looking for some advice for either obvious mistakes in my code or a way to access UICollectionViewCells that are neither in the list of visible items nor dequeued during scroll - there might be a way to access those cells i simplay don't know about
You can change this behavior if you turn off prefetching:
collectionView?.isPrefetchingEnabled = false
Or you can keep it on, but then either:
hook into UICollectionViewDelegate methods didEndDisplaying and willDisplay to know as cells appear and disappear independent of cellForItemAt; or
have cells do some KVO on some view controller property or observe some notification that the view controller will initiate in order to know whether to change their state.

Trigger a function when user taps `Back` in detail view controller

I need to refresh my collectionView when user returns to that VC because what he/she did in the detailVC has affect on the previous VC data. I tried collectionView.reloadData() in both viewDidLoad() and viewDidAppear() of my VC has the collectionView in it. And It came up that when user taps the 'Back' in detailVC both viewDidLoad() and viewDidAppear() do not work. So, I tried to call one of them in detailVC with instantiate the firstVC(which has the collectionView)
then I got an runtime error which said collectionView is nil. Any thoughts? (BTW, the segue between them is ShowPush, and I can not change it because I have to have the transition of this segue in my app.)
Here is the firstVC:
class SkillsController: UIViewController{
#IBOutlet weak var collectionView: UICollectionView!
var TAGS: [TAG] = []
override func viewDidLoad() {
super.viewDidLoad()
let nib = UINib(nibName: "TagCell", bundle: nil)
collectionView.register(nib, forCellWithReuseIdentifier: "tagCell")
self.sizingCell = (nib.instantiate(withOwner: nil, options: nil) as NSArray).firstObject as! TagCell?
self.loadMore()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
print("back to skills")
self.TAGS = TagManager.shared.tagList
collectionView.reloadData()
}
}
TAGS is my data which is stored in Realm database.
Here is the detailVC:
class SeeSelectedController: UICollectionViewController {
var TAGS: [TAG] = []
#IBOutlet weak var layout: FSQCollectionViewAlignedLayout!
override func viewDidLoad() {
super.viewDidLoad()
if currentTab.shared.isSkill {
self.title = "Selected Skills"
//init tags
let list = RealmManager.shared.skills
if let list = list {
for element in list {
TAGS.append(TAG(n: element.value!, iS: true))
}
}
collectionView?.reloadData()
}else{
self.title = "Selected Needs"
//init tags
let list = RealmManager.shared.needs
if let list = list {
for element in list {
TAGS.append(TAG(n: element.value!, iS: true))
}
}
collectionView?.reloadData()
}
let nib = UINib(nibName: "TagCell", bundle: nil)
collectionView?.register(nib, forCellWithReuseIdentifier: "tagCell")
}
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let item = TAGS[indexPath.row].name!
let currentState = TAGS[indexPath.row].isSelected!
TAGS[indexPath.row].isSelected = currentState ? false:true
if currentState {
print("deselect")
//remove from realm
RealmManager.shared.deleteItemFromList(type: getTypeOfTag(isSkill: currentTab.shared.isSkill), item: item)
}else{
print("select")
//add to realm
RealmManager.shared.addItemToList(type: getTypeOfTag(isSkill: currentTab.shared.isSkill), item: item)
}
if currentTab.shared.isSkill {
let VC: SkillsController = storyboard?.instantiateViewController(withIdentifier: "SkillsController") as! SkillsController
VC.viewDidAppear(true)
}
collectionView.reloadData()
//addd
}
}
So how it is working? in the SkillsVC user can select some tags from a pool, in the detailVC which is SeeSelecteVC he/she can drop selected tags. It is constantly changing in the Realm as you can see. The problem when user has dropped some tags in detailVC and press the Back button, the dropped tags are still looking as selected in SkillsVC. However when if user goes another VC and comes back to SkillsVC (by this way the viewDidLoad() is gonna work) the dropped tags are seems to be unselected. That's all.
If what you are looking for is just to reload on back button
What you can do is create your own custom UIBarButtonItem that will make you navigate backwards from your "detail view controller". What you should do next after adding your own back button is add an IBAction for UIBarBUttonItem and pop your "detail view controller".
Right before you do this, you should create a delegate that will be executed before the popping happens that will reload your UICollectionView.
The following is not the best way to achieve what you want:
In your didSelectItem for your second view controller, you are creating a new view controller here and you shouldn't force call viewDidAppear. Since you are creating a new UIViewController, you are not referencing the previous UIViewController that you came from and soo your UICollectionView is nil.
if currentTab.shared.isSkill {
//remove the below lines and call the delegate here
let VC: SkillsController = storyboard?.instantiateViewController(withIdentifier: "SkillsController") as! SkillsController
VC.viewDidAppear(true)
}
collectionView.reloadData()
What you should be doing is:
You should use delegates to send callbacks to previous view controllers or perform actions.
To create a delegate-
Using the first approach (using your own back button)-
protocol delegateVC{
func reloadCollectionView()
}
class SeeSelectedController: UICollectionViewController{
//add this inside this class
var delegate : delegateVC?
...
//implement your IBAction for back button and inside it-
... {
self.delegate.reloadCollectionView()
}
}
OR the second approach i pointed out (Just change your didSelectItem and it will reload the collectionView, no need to fret about back button at all and save the hassle, i strongly recommend this approach)
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let item = TAGS[indexPath.row].name!
let currentState = TAGS[indexPath.row].isSelected!
TAGS[indexPath.row].isSelected = currentState ? false:true
if currentState {
print("deselect")
//remove from realm
RealmManager.shared.deleteItemFromList(type: getTypeOfTag(isSkill: currentTab.shared.isSkill), item: item)
}else{
print("select")
//add to realm
RealmManager.shared.addItemToList(type: getTypeOfTag(isSkill: currentTab.shared.isSkill), item: item)
}
if currentTab.shared.isSkill {
self.delegate.reloadCollectionView()
}
}
}
And in your first view controller-
func reloadCollectionView(){
collectionView.reloadData()
}
Note: In your prepareForSegue remember to set the delegate of your detail view controller to be your first view controller

How to push a new view controller when a table cell is tapped?

I have created this table with 3 sections and 7 rows. The code is shown below
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet var subjectTabelView: UITableView!
var slSubject = ["English Lang&Lit", "Chinese Lang&Lit", "Economics"]
var hlSubject = ["Mathematics", "Chemistry", "Biology"]
var tokSubject = ["Theory of Knowledge"]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
subjectTabelView.dataSource = self
subjectTabelView.delegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 3
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0{
return hlSubject.count
}else if section == 1{
return slSubject.count
}else {
return tokSubject.count
}
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let subjectCell = tableView.dequeueReusableCellWithIdentifier("idSubjectCell", forIndexPath: indexPath) as! UITableViewCell
if indexPath.section == 0 {
subjectCell.textLabel?.text = hlSubject[indexPath.row]
} else if indexPath.section == 1{
subjectCell.textLabel?.text = slSubject[indexPath.row]
} else {
subjectCell.textLabel?.text = tokSubject[indexPath.row]
}
return subjectCell
}
func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if section == 0 {
return "HL"
} else if section == 1{
return "SL"
} else {
return "ToK"
}
}
}
What do I have to do to make every cell in this table pushes a new view controller when it is tapped? The picture of my storyboard is shown below. In my storyboard, my view controller, I have already created a navigation controller, and made the view controller that has the table the rootViewController. And for now, my tableView has only one prototype cell and one cell identifier.
Thank you!
Suppose your "locationVC" is:
class LocationVC: UIViewController {
#IBOutlet weak var fromWhereLabel: UILabel!
//This can be changed when creating this UIViewController
var textToShow : String?
override func viewWillAppear(animated: Bool) {
if let textToShow = textToShow {
fromWhereLabel.text = textToShow
}
}
}
then, just adding function below to your code in ViewController named UIViewController (that should have a better name ;-)) you can achieve your goal.
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
//if such cell exists and destination controller (the one to show) exists too..
if let subjectCell = tableView.cellForRowAtIndexPath(indexPath), let destinationViewController = navigationController?.storyboard?.instantiateViewControllerWithIdentifier("locationVC") as? LocationVC{
//This is a bonus, I will be showing at destionation controller the same text of the cell from where it comes...
if let text = subjectCell.textLabel?.text {
destinationViewController.textToShow = text
} else {
destinationViewController.textToShow = "Tapped Cell's textLabel is empty"
}
//Then just push the controller into the view hierarchy
navigationController?.pushViewController(destinationViewController, animated: true)
}
}
You will be able to have a LocationVC UIViewController launched every time you tap a cell, and it will have some value to prove it right. :)
Hope it Helps!
UPDATE: Code and Instructions below are for allowing to launch
different UIViewControllers after tap on cells
1.- Let's create a class that will be the parent for every one of our new UIViewControllers (the ones we are willing to go from our tableview cell's tap):
public class CommonDataViewController: UIViewController {
//Here we are going to be putting any data we want to share with this view
var data: AnyObject?
}
2.- Let's create some sort of Navigation rules, just to be organised ;-)
enum Navigation: Int {
case vc1 = 0, vc2 = 1, vc3 = 2, vc4 = 3
//How many rules we have (for not to exceed this number)
static let definedNavigations = 4
//This must return the identifier for this view on the Storyboard
func storyboardIdentifier() -> String {
//for this example's sake, we have a common prefix for every new view controller, if it's not the case, you can use a switch(self) here
return "locationVC_\(self.rawValue + 1)"
}
}
Now, let's build upon previous code:
3.- For clarity, let's change a little our previous LocationVC (that for this example, will have an Storyboard Identifier with the text "locationVC_1")
class LocationVC: CommonDataViewController {
#IBOutlet weak var fromWhereLabel: UILabel!
//This is optional, but improves clarity..here we take our AnyObject? variable data and transforms it into the type of data this view is excepting
var thisVCReceivedData: String? {
return data as? String
}
override func viewWillAppear(animated: Bool) {
if let textToShow = thisVCReceivedData {
fromWhereLabel.text = textToShow
}
}
}
4.- Now, we trigger all of this in our didSelectRowAtIndexPath function.
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
//Just to avoid tapping on a cell that doesn't have an UIViewController asociated
if Navigation.definedNavigations > indexPath.row {
//The view's instance on our Navigation enum to which we most go after tapping this cell
let nextView = Navigation(rawValue: indexPath.row)!
//The identifier of the destination CommonDataViewController's son in our Storyboard
let identifier = nextView.storyboardIdentifier()
//If everything exists...
if let subjectCell = tableView.cellForRowAtIndexPath(indexPath), let destinationViewController = navigationController?.storyboard?.instantiateViewControllerWithIdentifier(identifier) as? CommonDataViewController {
//here you can use a switch around "nextView" for passing different data to every View Controller..for this example, we just pass same String to everyone
if let text = subjectCell.textLabel?.text {
destinationViewController.data = text
} else {
destinationViewController.data = "Tapped Cell's textLabel is empty"
}
navigationController?.pushViewController(destinationViewController, animated: true)
}
}
}
Notice that you can achieve same results using protocols and delegate approach, this is just simpler to explain
Well to push a view controller in a UINavigationController you just use this code:
ViewController *viewController = [self.navigationController.storyboard instantiateViewControllerWithIdentifier:#"locationVC"];
[self.navigationController pushViewController:viewController animated:YES];
The method you are looking for is this one:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
ViewController *viewController = [self.navigationController.storyboard instantiateViewControllerWithIdentifier:#"locationVC"];
[self.navigationController pushViewController:viewController animated:YES];
}
You could use prepareForSegue method. You just need to set up the destination view. or the didselectrowatindexpath
prepareForSegue code looks like :
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "nameofTheSegue"
{
if let destinationVC = segue.destinationViewController as? OtherViewController{
// do whatever you want with the data you want to pass.
}
}
}

Resources