How to reinitialize table view override function? - ios

override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let storyboard = UIStoryboard(name: "FilteringPageSelection", bundle: nil)
controller = storyboard.instantiateViewControllerWithIdentifier("FilteringPageSelectionController") as! FilteringPageSelectionTableViewController
controller.filteringType = filterTitle[indexPath.row];
controller.selectedValue = selectedValue;
controller.title = "Selection"
self.navigationController?.pushViewController(controller, animated: true)
tableView.deselectRowAtIndexPath(indexPath, animated: true)
}
Above is my code, this is my table view controller code, i am intend to push to another view controller by clicking it cell. Inside the new controller, there is multiple value for me to choose. however , after i select the new value from the new controller, i back to the original table view scene and get the selected value from the new controller and assign into the variable selectedValue. Below is my code of the unwind segue.
#IBAction func unwindToFilteringVC(segue:UIStoryboardSegue) {
selectedValue = controller.selectedValue
}
However, when i click the cell again, since the tableview override function only initialize for the first time when the tableview is loaded, so when i change the value of the selectedValue, the controller.selectedValue still remain the old value and push into new controller.
How do i changes the value of controller.selectedValue?

func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) - this function gets called every time you tap on a cell, not just once. So whenever you select a cell, your code instantiates the filter view controller and sets its selectedValue to the current selectedValue of the table view controller (unless that is a global variable). Maybe that is not what you want.
OR
You may have mixed which selectedValue is which. You should rename one.

I think what you need to use is a delegate pattern to inform the table view controller of the selected value in the selection view controller instead of holding a reference to it. First you define a protocol:
protocol SelectionViewControllerDelegate: class {
func selectionViewController(controller: SelectionViewController, didSelectValue value: ValueType)
}
You add a weak reference to an optional property that conforms to that protocol and you inform the delegate of changes in the selected value right where you handle the selection action in your selection view controller.
class SelectionViewController: UIViewController {
weak var delegate: SelectionViewControllerDelegate?
func handleChange() {
delegate?.selectionViewController(self, didSelectValue: selectedValue)
}
}
Then you make your table view controller conform to this protocol like so:
class TableViewController: UITableViewController, SelectionViewControllerDelegate {
func selectionViewController(controller: SelectionViewController, didSelectValue value: ValueType) {
self.selectedValue = value
}
}
Don't forget to set the table view controller as the delegate of the selection view controller in the didSelectRowAtIndexPath method by saying:
controller.delegate = self
Right before you call navigationController?.pushViewController method.

Related

Passing data in reverse from viewcontroller to uitableviewcell [duplicate]

This question already has answers here:
Passing data between view controllers
(45 answers)
Closed 5 years ago.
I've got this problem, in the gif attached you can see it: if I tap on the row of UrgenzaViewController it gets back to Ho fumatoViewController, and what I need is that the Label in UITableViewCell "Urgenza" will be modified with the title of the row pressed in UrgenzaViewController. How to modify the label in the custom cell? Thanks everybody
In your Urgenza view controller create a delegate at the top of your file (above your class declaration, below the import statements) like this:
protocol UrgenzaDelegate: class {
func menuItemSelected(item: String)
}
Then inside your Urgenza class declaration create an instance of the delegate like this :
weak var delegate: UrgenzaDelegate?
Then inside didSelectRowAtIndexPath method I would call the delegate method like this:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let delegate = delegate {
delegate.menuItemSelected(item: dataSource[indexPath.row])
}
}
Replace 'dataSource' with whatever data source you are using to populate the cell labels.
Finally, in your initial view controller (Ho fumatoViewController) you need to conform to the delegate you just created. You can do this by making an extension like this :
extension fumatoViewController: UrgenzaDelegate {
func menuItemSelected(item: String) {
// Here is where you save the selected item to whatever data source you are using
tableView.reloadData()
}
}
And lastly, and very important!, wherever you are pushing the Urgenza view controller you must set yourself to its delegate property like so:
let vc = UrgenzaViewController()
vc.delegate = self // This is the important part!
self.present(vc, animated: true, completion: nil)

how to reference the Tab Bar Controller from a UICollectionView Cell

I have a tab bar controller application and in one of the tabs a UI Collection View Controller with an action assigned to a button. This button does its magic and then should change the tab bar view to another one. However, I'm not able to reference it right to the tab controller.
tabBarController is the class name assigned to the controller. So, I tried:
tabBarController.selectedIndex = 3
and also creating a method directly in the tabBarController class
tabBarController.goToIndex(3)
The error says: Instance member of 'goToIndex' cannot be used on type tabBarController
Any ideia?
Thank you,
Im having a little trouble understanding what you mean by referencing it right, but hopefully this will help. Assuming tabBarController is a subclass of UITabBarController:
class MyTabBarController: UITabBarController {
/// ...
func goToIndex(index: Int) {
}
}
In one of your tab controllers (UIViewController) you can reference your UITabBarController with self.tabBarController. Note that self.tabBarController is optional.
self.tabBarController?.selectedIndex = 3
If your tab UIViewController is a UIViewController inside a UINavigationController, then you will need to reference your tab bar like this:
self.navigationController?.tabBarController
To call a function on your subclass you would need to cast the tab bar controller to your custom subclass.
if let myTabBarController = self.tabBarController as? MyTabBarController {
myTabBarController.goToIndex(3)
}
Update based on comments:
You're correct that you cant access the tabBarController inside the cell unless you made it a property on either the cell itself (not recommended) or the app delegate. Alternatively, you could use target action on your UIViewController to call a function on the view controller every time a button is tapped inside a cell.
class CustomCell: UITableViewCell {
#IBOutlet weak var myButton: UIButton!
}
class MyTableViewController: UITableViewController {
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ReuseIdentifier", for: indexPath) as! CustomCell
/// Add the indexpath or other data as a tag that we
/// might need later on.
cell.myButton.tag = indexPath.row
/// Add A Target so that we can call `changeIndex(sender:)` every time a user tapps on the
/// button inside a cell.
cell.myButton.addTarget(self,
action: #selector(MyTableViewController.changeIndex(sender:)),
for: .touchUpInside)
return cell
}
/// This will be called every time `myButton` is tapped on any tableViewCell. If you need
/// to know which cell was tapped, it was passed in via the tag property.
///
/// - Parameter sender: UIButton on a UITableViewCell subclass.
func changeIndex(sender: UIButton) {
/// now tag is the indexpath row if you need it.
let tag = sender.tag
self.tabBarController?.selectedIndex = 3
}
}

What is the best design solution for this situation in iOS?

I have UITableView with two static cells. Each cell has custom class and independently validate account name, when I fill text field in the cell. (This part of code I got as is and I am not allowed to rewrite it). The cell delegates about changes if validation is correct to delegate (SocialFeedSelectCellDelegate). Originally, this tableView appeared in SignUpViewController: UITableViewController, UITableViewDataSource, UITableViewDelegate, SocialFeedSelectCellDelegate only.
Problem : The same UITableView should appear in two different places (SignUpViewController and SettingsViewController). Also SignUpViewController and SettingsViewController should know about success or fail of account validation.
What I tried : I created SocialFeedTableViewController: UITableViewController, SocialFeedSelectCellDelegate for the tableView with two cells. Set view in SocialFeedTableViewController as container view for SignUpViewController and SettingsViewController. I used second delegation (from SocialFeedTVC to SignUp and Settings) to notify SignUp and Settings about validation changes. I think it is bad idea, because of double delegation. Teammate said me that it is hard to understand.
Question: What is the best and simple design solution for the problem?
Why is the double delegation a problem? As far as I see it you have 2 table views, 1 for each controller. Then each controller sets the delegate to each of the table view as self. Even if not it is quite common to change the delegate of the object in runtime. It is also normal to have 2 delegate properties with the same protocol simply to be able to forward the message to 2 objects or more.
There are many alternatives as well. You may use the default notification center and be able to forward the messages this way. The only bad thing about it is you need to explicitly resign the notification listener from the notification center.
Another more interesting procedure in your case is creating a model (a class) that holds the data from the table view and also implements the protocol from the cells. The model should then be forwarded to the new view controller as a property. If the view controller still needs to refresh beyond the table view then the model should include another protocol for the view controller itself.
Take something like this for example:
protocol ModelProtocol: NSObjectProtocol {
func cellDidUpdateText(cell: DelegateSystem.Model.MyCell, text: String?)
}
class DelegateSystem {
class Model: NSObject, UITableViewDelegate, UITableViewDataSource, ModelProtocol {
// My custom cell class
class MyCell: UITableViewCell {
weak var modelDelegate: ModelProtocol?
var indexPath: NSIndexPath?
func onTextChanged(field: UITextField) { // just an example
modelDelegate?.cellDidUpdateText(self, text: field.text) // call the cell delegate
}
}
// some model values
var firstTextInput: String?
var secondTextInput: String?
// a delegate method from a custom protocol
func cellDidUpdateText(cell: DelegateSystem.Model.MyCell, text: String?) {
// update the appropriate text
if cell.indexPath?.row == 0 {
self.firstTextInput = text
} else {
self.secondTextInput = text
}
}
// table view data source
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 2
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = MyCell() // create custom cell
cell.indexPath = indexPath // We want to keep track of the cell index path
// assign from appropriate text
if cell.indexPath?.row == 0 {
cell.textLabel?.text = self.firstTextInput
} else {
cell.textLabel?.text = self.secondTextInput
}
cell.modelDelegate = self // set the delegate
return cell
}
}
// The first view controller class
class FirstViewController: UIViewController {
var tableView: UITableView? // most likely from storyboard
let model = Model() // generate the new model
override func viewDidLoad() {
super.viewDidLoad()
refresh() // refresh when first loaded
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
refresh() // Refresh each time the view appears. This will include when second view controller is popped
}
func refresh() {
if let tableView = self.tableView {
tableView.delegate = model // use the model as a delegate
tableView.dataSource = model // use the model as a data source
tableView.reloadData() // refresh the view
}
}
// probably from some button or keyboard done pressed
func presentSecondController() {
let controller = SecondViewController() // create the controller
controller.model = model // assign the same model
self.navigationController?.pushViewController(controller, animated: true) // push it
}
}
// The second view controller class
class SecondViewController: UIViewController {
var tableView: UITableView? // most likely from storyboard
var model: Model? // the model assigned from the previous view controller
override func viewDidLoad() {
super.viewDidLoad()
refresh() // refresh when first loaded
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
refresh() // Refresh each time the view appears. This will include when third view controller is popped
}
func refresh() {
if let tableView = self.tableView {
tableView.delegate = model // use the model as a delegate
tableView.dataSource = model // use the model as a data source
tableView.reloadData() // refresh the view
}
}
// from back button for instance
func goBack() {
self.navigationController?.popViewControllerAnimated(true)
}
}
}
Here the 2 view controllers will communicate with the same object which also implements the table view protocols. I do not suggest you to put all of this into a single file but as you can see both of the view controllers are extremely clean and the model takes over all the heavy work. The model may have another delegate which is then used by the view controllers themselves to forward additional info. The controllers should then "steal" the delegate slot from the model when view did appear.
I hope this helps you understand the delegates are not so one-dimensional and a lot can be done with them.

prepareforsegue vs performwithsegue if statements.

Suppose I have a table view, how would i go about creating code to have one cell go to one view controller and then have another cell to a second view controller. Specifically the prepare for segue and perform with segue if statements.
Your parent view controller (where the table view lives) should have your UITableViewDelegate methods.
You'll need to create your own implementation of:
optional func tableView(_ tableView: UITableView,
didSelectRowAtIndexPath indexPath: NSIndexPath)
And in there (which gets called when a cell is pressed), you'll want to call
self.performSegueWithIdentifier("Load View", sender: self)
One of the parameters passed into didSelectRowAtIndexPath is the index path row, so you know which row the user pressed. And you can pass along that cell (or the information, or the object that the cell is drawing it's contents from) in the "sender" parameter of the performSegueWithIdentifier method.
You'll have direct access to the child view controller from the parent in the prepareForSegue:sender: method. So you can do something like:
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
if (segue.identifier == "Load View") {
// pass data to next view
let cellData = sender as YourDataObject
let childViewController = segue.destinationViewController as YourChildViewController
// and now you can directly set whatever custom fields / bits
// you need to set in your destination view controller
}
}
Makes sense so far?

how to make my searchBar TableView perform a segue

I have created a TableView application following the "Beginning iPhone Development with Swift " book.The search Bar tableView is created with code and not within the storyboard.The book explains how to get search results and display the corresponding cells but I would like my app to perform a segue to a ViewController I have created in the storyBoard.How can I trigger a Segue with code ?
for more info , this is my file :
import UIKit
class SearchResultsController: UITableViewController , UISearchResultsUpdating{
let sectionsTableIdentifier = "section identifier"
var products = [product]()
var filteredProducts = [product]()
override func viewDidLoad() {
super.viewDidLoad()
tableView.registerClass(UITableViewCell.self,
forCellReuseIdentifier: sectionsTableIdentifier)
}
// MARK: - Table view data source
override func tableView(tableView: UITableView,
numberOfRowsInSection section: Int) -> Int {
return filteredProducts.count
}
override func tableView(tableView: UITableView,
cellForRowAtIndexPath indexPath: NSIndexPath)
-> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(
sectionsTableIdentifier) as UITableViewCell
cell.textLabel!.text = filteredProducts[indexPath.row].name
return cell }
// 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?) {
if segue.identifier == "detailView"{
let index = self.tableView?.indexPathForSelectedRow()
var destinationViewController : infoViewController = segue.destinationViewController as infoViewController
destinationViewController.Title = filteredProducts[index!.row].title
destinationViewController.eam = filteredProducts[index!.row].energy
destinationViewController.fam = filteredProducts[index!.row].fat
destinationViewController.pam = filteredProducts[index!.row].protein
destinationViewController.cam = filteredProducts[index!.row].carbohydrates
destinationViewController.imgName = filteredProducts[index!.row].imgName
}
}
func updateSearchResultsForSearchController(
searchController: UISearchController) {
let searchString = searchController.searchBar.text
filteredProducts.removeAll()
for prod in products{
var name = prod.name.lowercaseString
if name.rangeOfString(searchString) != nil {
filteredProducts.append(prod)
}
}
tableView.reloadData()
}}
Because the controller is built in code, you need to use the SearchResultsController's tableView delegate method didSelectRowAtIndexPath to trigger the presentation of the next view controller.
Assuming that there is a table view controller underpinning the SearchResultsController, you could potentially use that as the delegate of the SearchResultsController. The main table view controller might already have the necessary code to segue when a cell is selected, in which case you need to check which tableView has been selected in order to correctly determine which product the cell represents.
To set the delegate, add the following line to the code (in your comment above) where you create the SearchResultsController:
resultsController.tableView?.delegate = self
Then amend the didSelectRowAtIndexPath method to test which tableView is triggering the method:
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if (tableView == self.tableView) {
// use the existing code to present the detail VC, based on the data in the main table view
...
} else {
// use new code to present the detail VC, based on data from the SearchResultsController
...
}
}
If the main table view controller is in a storyboard, you can use a segue to present the detail VC. In this case you would use self.performSegueWithIdentifier() in the above code. If not, you would either use self.navigationController?.pushViewController() (if you are embedded in a navigation controller) or self.presentViewController() (to present the detail VC modally).
Another option would be to set the SearchResultsController's delegate to be self (in viewDidLoad), and then to implement didSelectRowAtIndexPath in the SearchResultsController class. In this case, you don't need to test which tableView has triggered the method, but you will not be able to use a segue.

Resources