configure tableview cell in previous controller - ios

I have got controller 1 with an imageview, uitextview and button. On button click there is an action to show controller with table view. I need to configure this image view and uitextview.text in my table view, but i don't understand how?
I tried to get access
func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
var header = tableView.dequeueReusableCellWithIdentifier("customHeader") as CustomHeaderCell
var controller = PreparePhotoViewController()
header.headerLabel.text = controller.textView.text//exc BAD ACCESS - textView is an outlet of controller, it is weak
header.headerPhoto.image = UIImage(named: "heart.png")
return header
}
but it has bad ACCESS exc, cause I think that uitextview with weak property is nil. So any advices how I can do my idea?

Add a property to the view controller you are going to be showing, such as:
var textToDisplay: String?
Then, before you push this new view, set that property, e.g.:
let viewController = [...]
viewController.textToDisplay = "Hello World"
self.presentViewController(viewController, animated: true, completion: nil)
Then, in your pushed view controller's viewDidLoad method, set the value of the UILabel's text property to your new self.textToDisplay value.
Hopefully that's the sort of thing you're thinking of?

I'm not sure about your approach, but I can see issue in your posted code. You are using wrong dequeue function.
Change tableView.dequeueReusableCellWithIdentifier to tableView.dequeueReusableHeaderFooterViewWithIdentifier().
func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
var header = tableView.dequeueReusableHeaderFooterViewWithIdentifier("customHeader") as CustomHeaderCell
...
return header
}
And you would probably want to use more accurate name CustomHeaderView, not CustomHeaderCell.

Related

Accessing UITableView Header View

I have a UITableView where the section header view contains a UITextField that the user can key in values. When I want to save the value that user keyed in, I need to access the table section header.
I tried using the method headerView(forSection:), and I put headerView(forSection: 0)! as a test to make sure that the section header definitely exists, but every time when I run the programme, the line where the method is returns an error "unexpectedly found nil while unwrapping an optional value".
I have also tried to tag the UITextField, but when I attempt to access the section header using certainTableView.viewWithTag(_ tag:), and cast the resulting UIView to UITextField, there is an error thrown at the line where casting happens, saying "Could not cast value of type 'UITableView' to 'UITextField'."
Both errors look pretty bizarre to me. In the first case, I made sure that the section header is existent, yet the method returned nil. In the second case, the returned tagged view should be of type UITableViewHeaderFooterView, yet the type is UITableView. Can someone suggest any possible reason why this is so and how to correct?
Or alternatively, is there any better approach to access the section header view so that I can save the text keyed in in the UITextField?
Below is the detail that I used in the first case.
In func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView?, I returned a UIView that contains a UITextField. In the table view which I need to save the keyed-in user data, I have a button that, when the user taps it, data are saved. Within that selector, I have the following:
//save item
let a = addItemTableView.headerView(forSection: 0)!
let b = a.subviews[0] as! UITextField
let c = b.text ?? ""
someNSManagedObject.text = c
The error I mentioned above appears at the first line.
After reading the post in this question, I got a hint and found out that, in func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? method, I should return a UITableViewHeaderFooterView instead of the previously UIView. As I changed the returned object's type to UITableViewHeaderFooterView, with everything else remaining the same, (i.e. add a UITextField to the UITableViewHeaderFooterView) I managed to access the UITextField in the UITableViewHeaderFooterView via headerView(forSection:) method.
To Be even clearer, given in the apple's documentation that the function declaration is func headerView(forSection section: Int) -> UITableViewHeaderFooterView?, showing that the returned value is UITableViewHeaderFooterView, in order to access the header view using the method, the returned value from func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? should be UITableViewHeaderFooterView instead of any other generic UIView or UIView subclasses.
Hope this helps.
First add a property to your model textFieldData to save textField data in.
Then in your HeaderViewClass add a closure var textFieldDidChange: ((String) -> Void)?
class HeaderViewClass: UITableViewHeaderFooterView {
// MARK:- IBOutlets
#IBOutlet weak var textField: UITextField!
// MARK:- Variable
var textFieldDidChange: ((String) -> Void)?
// MARK:- Functions
func fill(with model: Model) {
textField.delegate = self
textField.text = model.textFieldData
}
}
extension HeaderViewClass: UITextFieldDelegate {
func textFieldDidChange(_ textField: UITextField) {
textFieldDidChange?(textField.text)
}
}
In your viewController class
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
let footerView = tableView.dequeueReusableHeaderFooterView(withIdentifier: PlaceOrderFooterView.id) as! PlaceOrderFooterView
footerView.fill(with: models[section])
footerView.textFieldDidChange = { [unowned self] text in
self.models[section].textFieldData = text
}
return footerView
}
Now your model object has the updated value whenever any textField data is changed.
In your method
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {}
when you return your custom header containing UITextField , assign it's delegate to your controller, this way you'll get all keyed values by user under UITextFieldDelegate function

How to use UITableView for TextField input view

I currently have an app which uses UIPickerViews to allow users to select which answer they want for a text field (to avoid spelling mistakes etc).
However, I have found that the UIPickerView isn't really what I want to use because I haven't had great feedback from it when testing.
I have done some research into how to use a UITableView for text field inputs instead, so when the user clicks the Textfield, the user segues to a UITableView with the same options which would be provided by the UIPickerView. Once they click the cell with the option they are looking for it would segue back to the form with the result chosen inside the text field. I thought this would be a better user experience as I could also implement the search to help users narrow down the option they require quicker.
I have been trying to get this to work for a while now, but I'm quite new at coding and haven't been able to crack it yet. I would just like advice on how to approach this? I'm using Swift and the Storyboard to create my app.
Would I need to create a separate ViewController with a UITableView that loads the options and then move the value back to the form once the cell is clicked?
One approach would be to use a table view in separate view controller. lets call it choiceVC and pass data which text field was tapped.
Then send the data back to your form to show what user has selected.
Follow these steps
Detect user tap on text field and segue to choiceVC by implementing this delegate function of UITextField
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
//push your choiceVC and pass data
var textFeildTapped = ""
//note: chooseGameTextField should be text field's outlet
if textField == chooseGameTextField{
textFeildTapped = "games"
}else if textField == chooseActiviyTextField {
textFeildTapped = "activiy"
}
//first set identifier of your view controller by going to identity inspector and setting the value StoryBoard ID
if let controller = storyboard?.instantiateViewController(withIdentifier: "choiceVC") as? ProductDetailVc{
//pass which text field was tapped
controller.choicesToShow = textFeildTapped
navigationController?.pushViewController(controller, animated: true)
}
return true
}
Note: choiceVC should have a variable "choicesToShow" of type string
in viewdidload of choiceVC check the variable
if choicesToShow == "games" {
//populate your table view with games.
}else {
//check for other cases and proceed accordingly
//activiy, console etc
}
Implement didSelect delegate method of UITableView
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
//Pass data back to your form using **delegate pattern**. see link in notes below
}
Notes:
See screen shot of setting storyBoard ID
see how to implement table view if you dont know
https://stackoverflow.com/a/33234181/7698092
See how to pass data backward using delegate pattern
https://medium.com/#mayooresan/passing-data-between-viewcontrollers-via-delegate-protocols-4ecde4b167de
If you are looking for an alternative for picker view to select options you can use dropdown like controls Eg.
DropDown
RSSelectionMenu
I hope these libraries can solve your issues, best of luck
I hope this code work for you. it's wroking for me.
First view controller for textfiled form where you want to open tableview.for that use textfield Delegate.
First View Controller
func doSomething(text: UITextField, with data: String) {
text.text = data
}
func textFieldDidBeginEditing(_ textField: UITextField) {
let objGametableVC = self.storyboard?.instantiateViewController(withIdentifier: "GametableVC") as! GametableVC;
objGametableVC.delegate = self
objGametableVC.selectedTextField = textField
if textField == txtActivity{
objGametableVC.tblData.removeAll()
objGametableVC.tblData = ["act1","act2"]
}
else if textField == txtGameName{
objGametableVC.tblData.removeAll()
objGametableVC.tblData = ["gam1","game2"]
}
textField.resignFirstResponder()
self.navigationController?.pushViewController(objGametableVC, animated: true);
}
Second view Controller For tableview show and pass data from second to first controller
Second view Controller
var delegate: Delegate?
var tblData: [String] = [String]()
var selectedTextField:UITextField? = nil
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return tblData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let tblcell = tableView.dequeueReusableCell(withIdentifier: "gamelblCell", for: indexPath) as! gamelblCell
tblcell.lblName.text = tblData[indexPath.row]
return tblcell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let tblcell = tableView.cellForRow(at: indexPath) as! gamelblCell
let data = tblcell.lblName.text
delegate?.doSomething(text: selectedTextField!, with: data!)
self.navigationController?.popViewController(animated: true)
}

UITableViewCells not appearing in second Tab

I have the following problem:
I am making a Pokédex-like application that displays a list of all 721 Pokémon on the first tab, and another list on the second tab containing My Favorite Pokémon. Essentially, there are two identical ViewControllers connected to my TabBar.
My storyboard is as follows:
So here is the problem:
The TableView on the first (and initial) tab works fine. However, when I load the TableView on the second tab the Pokémon are loaded, but not displayed. I am able to click the TableViewCell and go to the detail page, but the label in the TableViewCell is not showing anything.
This is the code I use for loading Favorites TableView
class FavoritesViewController: BaseViewController,
UITableViewDataSource, UITableViewDelegate {
#IBOutlet var FavoritesListView: UITableView!
var pokemonList: [String] = ["Nothing Here!"]
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("FavoriteCell", forIndexPath: indexPath) as! FavoriteCell
var name = pokemonList[indexPath.row]
capitalizeFirstLetter(&name)
cell.nameLabel.text = name
return cell;
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return pokemonList.count
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
print(pokemonList[indexPath.row])
self.performSegueWithIdentifier("ToPokemonDetail", sender: pokemonList[indexPath.row])
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "ToPokemonDetail"){
let destination = segue.destinationViewController as! PokemonDetailViewController
let thisPokemon = sender as! String
destination.currentPokemon = thisPokemon
}
}
override func viewWillAppear(animated: Bool) {
FavoritesListView.reloadData()
}
override func viewDidLoad() {
super.viewDidLoad()
// Fetch the cached list, getNames returns an array of strings
let list = utility.getNames("Favorites")
pokemonList = list
}
The delegate and the dataSource are set via the storyboard.
The above code works, and shows the Favorites list just fine. The class for the complete Pokédex has a similar construction.
I have tried switching Favorites and Pokédex around, so that it shows the complete Pokémon list on startup. All 721 Pokémon are shown correctly, but then the Favorites are not visible.
What else I have tried:
Checking the Reuse Identifiers, over and over
Referencing outlets should be bound correctly
Calling TableView.reloadData() in the viewDidAppear method
Switching around the tab items
Does anyone have any clue what on earth is going on here?
Feel free to ask any more questions
Edit: this is what happens when I swap the two TabBar Buttons around, no code changes
Pokédex Screen
Favorites Screen
GitHub Project Here
Problem is in storyboard cell label frame. Set constraints of view controller for (Any,Any) Size Class. I can commit the code on github if you can give me write rights on your git. Thanks
Perhaps your table's delegate and dataSource are not set.
table.delegate = self
table.dataSource = self
Of course this is after you add the properties to your view controller
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
Your number of rows is always 0 for that controller,
I looked into your code pokemonList count is always 0 its not updating data in it
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return pokemonList.count
}
The big issue is your PokemonDetailViewController is not a UITableViewController. It needs to inherent from UITableViewDataSource, UITableViewDelegate and then be connected to the storyboard view to provide data and formatting for a table.

How to reinitialize table view override function?

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.

How to trigger a pickerview when select a cell in a tableView

I have a tableView with 7 cells like this:
I wanna trigger some events when you select a cell. For example, start editing the username when you tap the Username row. And pop up a picker view at the bottom with Male/Female selection inside when you tap the Gender row.
As far as I know, I need to put those events inside this:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
}
But I have no idea how to accomplish this. Anyone has ideas? Thank you in advance.
Basically, you can make each cell has its own picker view.
open class DatePickerTableViewCell: UITableViewCell {
let picker = UIDatePicker()
open override func awakeFromNib() {
super.awakeFromNib()
picker.datePickerMode = UIDatePickerMode.date
}
open override var canBecomeFirstResponder: Bool {
return true
}
open override var canResignFirstResponder: Bool {
return true
}
open override var inputView: UIView? {
return picker
}
...
}
And then in your didSelectRowAt, just make the cell becomeFirstResponder:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let cell = tableView.cellForRow(at: indexPath) as? DatePickerTableViewCell {
if !cell.isFirstResponder {
_ = cell.becomeFirstResponder()
}
}
}
You can check my library for detail:
https://github.com/hijamoya/PickerViewCell
You are correct. Putting the logic in didSelectRowAtIndexPath is a good way to go.
How you do it is to write code. There is no stock answer.
If you want content to appear on top of the current window then you will need to handle that yourself. On iPad, you could use a popover, but popovers are not supported natively on iPhone/iPod touch. You might look at using a 3rd party popover library that offers popover support for iPhone. There are several on Github, and probably several on Cocoa Controls as well. I've used one before, but it had a few issues, so I wouldn't recommend it.
If you are ok presenting a whole new view controller then simply define a new view controller in your storyboard, give it a unique identifier, use instantiateViewControllerWithIdentifier to create it, then presentViewController:animated: to display it modally.
UIPickerView is subclass of UIView, so you can add and use it same like any other UIView object. for your specific recquirment you should create an object of UIPickerView and show and hide it when necessary.
So create a UIPickerView and add above the table view inside view in which you added tableView and in didSelectRowAtIndexPath set pickerView.hidden = false
And also you can animate it from bottom via
UIView.animateWithDuration(1, animations: { () -> Void in
// And set final frame here
})

Resources