how to implement one radio button to be active while selecting it? - ios

I am having table view and on table view cell the radio button has been placed and the radio buttons may increase or decrease dynamically when I select one radio button the other buttons should not active at a time here is the code I used for selecting a button but I am unable to make other button inactive at a time
func paymentMethodURL() {
let url = NSURL(string: self.paymentmethodURL)
URLSession.shared.dataTask(with: (url as URL?)!, completionHandler: {(data, response, error) -> Void in
if let jsonObj = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? NSDictionary {
self.paymentmethodsArray = (jsonObj!.value(forKey: "payment method") as? [[String: AnyObject]])!
OperationQueue.main.addOperation({
self.tableDetails.reloadData()
})
}
}).resume()
}
func paymentreviewURL() {
let url = NSURL(string: self.paymentReviewURL)
URLSession.shared.dataTask(with: (url as URL?)!, completionHandler: {(data, response, error) -> Void in
if let jsonObj = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? NSDictionary {
let arrayss = jsonObj?.allKeys
OperationQueue.main.addOperation({
})
}
}).resume()
}
#IBAction func selectRadioButton(_ sender: KGRadioButton) {
sender.isSelected = !sender.isSelected
if sender.isSelected {
} else{
}
}
func numberOfSections(in tableView: UITableView) -> Int{
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return paymentmethodsArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let paymentcell = tableView.dequeueReusableCell(withIdentifier: "cell",for:indexPath) as! paymentTableViewCell
tableDetails.isHidden = false
myActivityIndicator.stopAnimating()
let arr = self.paymentmethodsArray[indexPath.row]
paymentcell.paymentNameLabel.text = arr["name"]as? String
return paymentcell
}

you should like this:
var tagSelected = -1
//assume each radio button have tag 0 1 2
#IBAction func selectRadioButton(_ sender: KGRadioButton) {
tagSelected = sender.tag
tableView.reloadData()
}
//cell for RowAtIndexPath
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier:
kCellReuseIdentifier, for: indexPath) as? YourCell else {
fatalError("Cell is not dequeued")
}
//logic for match indexPath.row /section to your button tags
if indexPath.row == tagSelected {
//selected here your radio button
} else {
//unselect all others
}
//other code
return cell
}

var checkIsRadioSelect = [Int]()
var webserviceArray = [modelObj]()
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let obj = webserviceArray[indexPath.row]
cell.radioButton.tag = indexPath.row
let checkIndex = self.checkIsRadioSelect.index(of: obj.wateverUniqeId)
if(checkIndex != nil){
cell.radioButton.isSelected = true
}else{
cell.radioButton.isSelected = false
}
}
#IBAction func selectRadioButton(_ sender: UIButton) {
let obj = webserviceArray[sender.tag]
let chekIndex = self.checkIsRadioSelect.index(of:obj.wateverUniqeId)
if sender.isSelected {
self.checkIsRadioSelect.remove(at: sender.tag)
} else{
if(chekIndex == nil){
self.checkIsRadioSelect.append(obj.wateverUniqeId)
}
} self.tableview.reloadData()
}

pretty simple solution is to have an integer for exemple to hold the index of the cell containing the selected button, for exemple at t=0 none is selected so say initial value for this new variable would be -1, once the user select button in cell 3 , u put 3 in that new variable , and later when he select cell 5 first you check if the variable has a valid index in it (in this case 3) then you go unselect the radio in cell 3 (which u have its index) and then select cell 5 and so on and so on..
now in your case I take it that IBAction is inside you cell controller, so you dont have direct access to the tableview and other cells to "deselect" if needed, what I would do is create a delegate protocol for that cell and have the viewcontroller containing the tableview be its delegate so whenever the user select a button i just invoke the delegate method with the cell's index as parameter and obviously implement this delegate method in the viewcontroller and do as I said earlier, here s an exemple on how it would go :
in the cell controller :
#IBAction func selectRadioButton(_ sender: KGRadioButton) {
if sender.isSelected {
} else{
self.delegate?.selectButton(index: self.index)
//invoke the delegate so that the viewcontroller deselect any other cell that might be selected
}
}
and in the viewcontroller :
func selectButton(index: index) {
if self.selectedCell != -1 {
let cell = self.tableView.cellForRowAtIndex(IndexPath(0, self.selectedCell))
cell.selectRadioButton(cell.button)
}
self.selectedCell = index
}

Related

How to optimize loading from Firestore to Tableview

My View Controller has a Tableview with 2 segments. Depending on which Segment is selected, the Tableview displays a different set of data.
#IBAction func didChangeSegment(_ sender: UISegmentedControl) {
if sender.selectedSegmentIndex == 0 {
model.getRecipes(starredTrue: false)
}
else if sender.selectedSegmentIndex == 1 {
model.getRecipes(userAdded: true)
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return recipe.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MealPlanCell", for: indexPath) as! MealPlanCell
let recipeInTable = recipe[indexPath.row]
cell.displayRecipe(recipe: recipeInTable, indexPathRow: indexPath.row)
return cell
}
And this is how model.getRecipes() gets data from Firestore before returning it to the Tableview:
let recipeQuery = db.collection("recipes")
let docRef = recipeQuery.document(documentId)
docRef.getDocument { document, error in
if let error = error as NSError? {
self.errorMessage = "Error getting document: \(error.localizedDescription)"
}
else {
if let document = document {
do {
self.recipe = try document.data(as: Recipe.self)
let recipeFromFirestore = Recipe(
id: documentId,
title: self.recipe!.title ?? "")
self.recipes.append(recipeFromFirestore)
}
catch {
}
}
DispatchQueue.main.async {
self.delegateRecipes?.recipesRetrieved(recipes: self.recipes)
}
}
}
The issue I'm having is that the Tableview takes a very long time to display data. It appears this is because it has to wait for the model to finish loading all the data from Firestore every time I select one of the segments.
How can I optimize this process? Is it possible to have the TableView load/display cell by cell, instead of needing to wait for all data to be loaded?
Any guidance is much appreciated!

Error setting UILabel text in custom UITableViewCell

I am quite new to Swift programming, but I am having trouble setting UILabel text in my UITableView class for individual UITableViewCell instances.
I have created a custom subclass of UITableViewCell called PizzaTableViewCell and a custom UITableView class called PizzaListTableViewController. I am trying to populate the UITableView instance with data from an array, which is being populated from an API call to my node.js server.
I have included my UITableView subclass, custom UITablveViewCell class, the struct for the data, and a link to a screenshot of the Simulator loading what I have done. Any help is greatly appreciated!
I have verified that the data is being put in the array with no issues, as I can print the contents after the call to fetchInventory method. I have been able to set a single textLabel with
cell.textLabel?.text = pizzas[indexPath.row].name
along with an image in the array with:
cell.imageView?.image = pizzas[indexPath.row].image
but I have 2 more labels that I need in each cell which I cannot set. I have checked my IBOutlets and Storyboard identifiers, and they match the code.
class PizzaListTableViewController: UITableViewController {
var pizzas: [Pizza] = []
override func viewDidLoad() {
super.viewDidLoad()
//title you will see on the app screen at the top of the table view
navigationItem.title = "Drink Selection"
tableView.register(PizzaTableViewCell.self, forCellReuseIdentifier: "Pizza")
//tableView.estimatedRowHeight = 134
//tableView.rowHeight = UITableViewAutomaticDimension
fetchInventory { pizzas in
guard pizzas != nil else { return }
self.pizzas = pizzas!
print(self.pizzas)
//self.tableView.reloadData()
//print(self.pizzas)
DispatchQueue.main.async { [weak self] in
self?.tableView.reloadData()
}
}
} //end of viewDidLoad
private func fetchInventory(completion: #escaping ([Pizza]?) -> Void) {
Alamofire.request("http://127.0.0.1:4000/inventory", method: .get)
.validate()
.responseJSON { response in
guard response.result.isSuccess else { return completion(nil) }
guard let rawInventory = response.result.value as? [[String: Any]?] else { return completion(nil) }
let inventory = rawInventory.compactMap { pizzaDict -> Pizza? in
var data = pizzaDict!
data["image"] = UIImage(named: pizzaDict!["image"] as! String)
//print(data)
//print("CHECK")
print("Printing each item: ", Pizza(data: data))
//printing all inventory successful
return Pizza(data: data)
}
completion(inventory)
}
}
#IBAction func ordersButtonPressed(_ sender: Any) {
performSegue(withIdentifier: "orders", sender: nil)
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
//PRINTING ROWS 0 TWICE in console
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print("ROWS", pizzas.count)
return self.pizzas.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: PizzaTableViewCell = tableView.dequeueReusableCell(withIdentifier: "Pizza", for: indexPath) as! PizzaTableViewCell
//cell.backgroundColor = Services.baseColor
//cell.pizzaImageView?.image = pizzas[indexPath.row].image
//THESE WORK BUT ARE A STATIC WAY OF SETTING THE CELLS
//CAN ONLY SET THE SELL WITH A SINGLE TEXT LABEL FROM THE DATA ARRAY
cell.imageView?.image = pizzas[indexPath.row].image
cell.textLabel?.text = pizzas[indexPath.row].name
//cell.textLabel?.text = pizzas[indexPath.row].description
//cell.textLabel?.text = "$\(pizzas[indexPath.row].amount)"
// cell.name?.text = pizzas[indexPath.row].name
// cell.imageView?.image = pizzas[indexPath.row].image
// cell.amount?.text = "$\(pizzas[indexPath.row].amount)"
// cell.miscellaneousText?.text = pizzas[indexPath.row].description
//print(cell.name?.text! as Any)
print(cell.imageView as Any)
return cell
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 100.0
} //END OF
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "pizzaSegue", sender: self.pizzas[indexPath.row] as Pizza)
} //END OF override func tableView
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "pizzaSegue" {
guard let vc = segue.destination as? PizzaViewController else { return }
vc.pizza = sender as? Pizza
}
} //END OF override preppare func
}
class PizzaListTableViewController: UITableViewController {
var pizzas: [Pizza] = []
override func viewDidLoad() {
super.viewDidLoad()
//title you will see on the app screen at the top of the table view
navigationItem.title = "Drink Selection"
tableView.register(PizzaTableViewCell.self, forCellReuseIdentifier: "Pizza")
//tableView.estimatedRowHeight = 134
//tableView.rowHeight = UITableViewAutomaticDimension
fetchInventory { pizzas in
guard pizzas != nil else { return }
self.pizzas = pizzas!
print(self.pizzas)
//self.tableView.reloadData()
//print(self.pizzas)
DispatchQueue.main.async { [weak self] in
self?.tableView.reloadData()
}
}
} //end of viewDidLoad
private func fetchInventory(completion: #escaping ([Pizza]?) -> Void) {
Alamofire.request("http://127.0.0.1:4000/inventory", method: .get)
.validate()
.responseJSON { response in
guard response.result.isSuccess else { return completion(nil) }
guard let rawInventory = response.result.value as? [[String: Any]?] else { return completion(nil) }
let inventory = rawInventory.compactMap { pizzaDict -> Pizza? in
var data = pizzaDict!
data["image"] = UIImage(named: pizzaDict!["image"] as! String)
//print(data)
//print("CHECK")
print("Printing each item: ", Pizza(data: data))
//printing all inventory successful
return Pizza(data: data)
}
completion(inventory)
}
}
#IBAction func ordersButtonPressed(_ sender: Any) {
performSegue(withIdentifier: "orders", sender: nil)
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
//PRINTING ROWS 0 TWICE in console
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print("ROWS", pizzas.count)
return self.pizzas.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: PizzaTableViewCell = tableView.dequeueReusableCell(withIdentifier: "Pizza", for: indexPath) as! PizzaTableViewCell
//cell.backgroundColor = Services.baseColor
//cell.pizzaImageView?.image = pizzas[indexPath.row].image
//THESE WORK BUT ARE A STATIC WAY OF SETTING THE CELLS
//CAN ONLY SET THE SELL WITH A SINGLE TEXT LABEL FROM THE DATA ARRAY
cell.imageView?.image = pizzas[indexPath.row].image
cell.textLabel?.text = pizzas[indexPath.row].name
//cell.textLabel?.text = pizzas[indexPath.row].description
//cell.textLabel?.text = "$\(pizzas[indexPath.row].amount)"
// cell.name?.text = pizzas[indexPath.row].name
// cell.imageView?.image = pizzas[indexPath.row].image
// cell.amount?.text = "$\(pizzas[indexPath.row].amount)"
// cell.miscellaneousText?.text = pizzas[indexPath.row].description
//print(cell.name?.text! as Any)
print(cell.imageView as Any)
return cell
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 100.0
} //END OF
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "pizzaSegue", sender: self.pizzas[indexPath.row] as Pizza)
} //END OF override func tableView
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "pizzaSegue" {
guard let vc = segue.destination as? PizzaViewController else { return }
vc.pizza = sender as? Pizza
}
} //END OF override preppare func
}
struct Pizza {
let id: String
let name: String
let description: String
let amount: Float
//let amount: String
let image: UIImage
init(data: [String: Any]) {
//print("CHECK:: pizza.swift")
self.id = data["id"] as! String
self.name = data["name"] as! String
// self.amount = data["amount"] as! Float
self.amount = ((data["amount"] as? NSNumber)?.floatValue)!
self.description = data["description"] as! String
self.image = data["image"] as! UIImage
}
}
As noted above, I have been able to print the contents of the data array with beer names, pictures, descriptions and etc. I have tried to print to console
print(cell.name?.text)
after setting
cell.name?.text = pizzas[indexPath.row].name
but it prints nil and this is a problem. I have been stuck with this for about 2 weeks!
IBOutlets screenshot:
I think i found your Problem, let me explain
What you are doing here is you have a custom UITableViewCell defined in the Storyboard in a Controller named "Root View Controller" which is not your PizzaListTableViewController to put it simply
And as you said you have absolutely no issue regarding the IBOutlets
Now when you say
tableView.register(PizzaTableViewCell.self, forCellReuseIdentifier: "Pizza")
In Your PizzaListTableViewController you are not linking it with the UI of the cell rather just the Code (This is only used when there is no xib of the cell)
Now what you can do to solve this
Solution # 1
Move/Copy your UI of the PizzaTableViewCell to PizzaListTableViewController in the storyboard from your "Root View Controller"
Make sure you add a Reuse Identifier in the Attribute Inspector of the cell in the storyboard
remove tableView.register(PizzaTableViewCell.self, forCellReuseIdentifier: "Pizza") this wont give you an error this time as it will automatically get register
Make sure all the IBOutlets are connected
Solution # 2
create a separate Nib (xib) of the cell
and now you have to register the cell here like
tableView.register(UINib(nibName: "PizzaTableViewCell", bundle: Bundle.main), forCellReuseIdentifier: "PizzaCell")
Hope this helps.
Try this
cell.name?.text = ...
cell.amount?.text = ...
cell.miscellaneousText?.text = ...
cell.pizzaImageView?.image = ...
If it still does not work then make sure your cell and your outlets are not null when setting its value. Hope it helps !
There is something definitely strange going on with your setup.
If you try to name the IBOutlets with the same name as the UITableViewCell default property it'll throw an error. The fact that you were able to set those names and build successfully is strange.
From the screenshot above you can see what happens when I attempted to do this.
Make sure your Table View Controller class is set in the storyboard.
Make sure your Table View Cell class is set in the storyboard.
Make sure that all your outlets are properly connected.
Make sure your Table View Cell Identifier is provided in the storyboard.
My Table View Controller Subclass
My Table View Cell Subclass
cell.imageView?.image and cell.textLabel?.text are optional properties of the table view itself. They are not the properties of the custom cell that you designed.
You use tableView.register(PizzaTableViewCell.self, forCellReuseIdentifier: "Pizza") when you have designed a table view cell in XIB. But as you have designed the cell in the storyboard itself you should set the cell reuse identifier and cell class in the storyboard.
I hope this will help you out.

Limit the amount of cells shown in tableView, load more cells when scroll to last cell

I'm trying to set up a table view that only shows a specific amount of cells. Once that cell has been shown, the user can keep scrolling to show more cells. As of right now I'm retrieving all the JSON data to be shown in viewDidLoad and storing them in an array. Just for example purposes I'm trying to only show 2 cells at first, one the user scrolls to bottom of screen the next cell will appear. This is my code so far:
class DrinkViewController: UIViewController {
#IBOutlet weak var drinkTableView: UITableView!
private let networkManager = NetworkManager.sharedManager
fileprivate var totalDrinksArray: [CocktailModel] = []
fileprivate var drinkImage: UIImage?
fileprivate let DRINK_CELL_REUSE_IDENTIFIER = "drinkCell"
fileprivate let DRINK_SEGUE = "detailDrinkSegue"
var drinksPerPage = 2
var loadingData = false
override func viewDidLoad() {
super.viewDidLoad()
drinkTableView.delegate = self
drinkTableView.dataSource = self
networkManager.getJSONData(function: urlFunction.search, catagory: urlCatagory.cocktail, listCatagory: nil, drinkType: "margarita", isList: false, completion: { data in
self.parseJSONData(data)
})
}
}
extension DrinkViewController {
//MARK: JSON parser
fileprivate func parseJSONData(_ jsonData: Data?){
if let data = jsonData {
do {
let jsonDictionary = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.mutableContainers) as? [String : AnyObject]//Parses data into a dictionary
// print(jsonDictionary!)
if let drinkDictionary = jsonDictionary!["drinks"] as? [[String: Any]] {
for drink in drinkDictionary {
let drinkName = drink["strDrink"] as? String ?? ""
let catagory = drink["strCategory"] as? String
let drinkTypeIBA = drink["strIBA"] as? String
let alcoholicType = drink["strAlcoholic"] as? String
let glassType = drink["strGlass"] as? String
let drinkInstructions = drink["strInstructions"] as? String
let drinkThumbnailUrl = drink["strDrinkThumb"] as? String
let cocktailDrink = CocktailModel(drinkName: drinkName, catagory: catagory, drinkTypeIBA: drinkTypeIBA, alcoholicType: alcoholicType, glassType: glassType, drinkInstructions: drinkInstructions, drinkThumbnailUrl: drinkThumbnailUrl)
self.totalDrinksArray.append(cocktailDrink)
}
}
} catch let error as NSError {
print("Error: \(error.localizedDescription)")
}
}
DispatchQueue.main.async {
self.drinkTableView.reloadData()
}
}
//MARK: Image Downloader
func updateImage (imageUrl: String, onSucceed: #escaping () -> Void, onFailure: #escaping (_ error:NSError)-> Void){
//named imageData because this is the data to be used to get image, can be named anything
networkManager.downloadImage(imageUrl: imageUrl, onSucceed: { (imageData) in
if let image = UIImage(data: imageData) {
self.drinkImage = image
}
onSucceed()//must call completion handler
}) { (error) in
onFailure(error)
}
}
}
//MARK: Tableview Delegates
extension DrinkViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
//return numberOfRows
return drinksPerPage
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = drinkTableView.dequeueReusableCell(withIdentifier: DRINK_CELL_REUSE_IDENTIFIER) as! DrinkCell
//get image from separate url
if let image = totalDrinksArray[indexPath.row].drinkThumbnailUrl{//index out of range error here
updateImage(imageUrl: image, onSucceed: {
if let currentImage = self.drinkImage{
DispatchQueue.main.async {
cell.drinkImage.image = currentImage
}
}
}, onFailure: { (error) in
print(error)
})
}
cell.drinkLabel.text = totalDrinksArray[indexPath.row].drinkName
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let image = totalDrinksArray[indexPath.row].drinkThumbnailUrl{
updateImage(imageUrl: image, onSucceed: {
}, onFailure: { (error) in
print(error)
})
}
performSegue(withIdentifier: DRINK_SEGUE, sender: indexPath.row)
}
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
let lastElement = drinksPerPage
if indexPath.row == lastElement {
self.drinkTableView.reloadData()
}
}
}
I saw this post: tableview-loading-more-cell-when-scroll-to-bottom and implemented the willDisplay function but am getting an "index out of range" error.
Can you tell me why you are doing this if you are getting all results at once then you don't have to limit your display since it is automatically managed by tableview. In tableview all the cells are reused so there will be no memory problem. UITableViewCell will be created when it will be shown.
So no need to limit the cell count.
I dont now what you are doing in your code but:
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
let lastElement = drinksPerPage // no need to write this line
if indexPath.row == lastElement { // if block will never be executed since indexPath.row is never equal to drinksPerPage.
// As indexPath starts from zero, So its value will never be 2.
self.drinkTableView.reloadData()
}
}
Your app may be crashing because may be you are getting only one item from server.
If you seriously want to load more then you can try this code:
Declare numberOfItem which should be equal to drinksPerPage
var numberOfItem = drinksPerPage
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
//return numberOfRows
return numberOfItem
}
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
if indexPath.row == numberOfItem - 1 {
if self.totalDrinksArray.count > numberOfItem {
let result = self.totalDrinksArray.count - numberOfItem
if result > drinksPerPage {
numberOfItem = numberOfItem + drinksPerPage
}
else {
numberOfItem = result
}
self.drinkTableView.reloadData()
}
}
}

create a group from selected tableview cells

I am trying to send the checked rows from my table view into a newly created group to my firebase database, once the user selects the rows and the checkmark is displayed, how can I send the selected rows information to my database. I am not sure if I should be doing this in the didSelectRowAt function or to add a "create group" button once the rows are selected. Any information will be helpful thank you.
import UIKit
class FriendsViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var userList = [Users]()
#IBOutlet weak var myTableView: UITableView!
final let urlString = "https://api.lookfwd.io/v1/test/users"
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return userList.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let myCell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! MyTableViewCell
myCell.selectionStyle = UITableViewCellSelectionStyle.none
myCell.nameLabel.text = userList[indexPath.row].name
return myCell
}
override func viewDidLoad() {
super.viewDidLoad()
self.downloadJsonWithTask()
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if myTableView.cellForRow(at: indexPath)?.accessoryType == UITableViewCellAccessoryType.checkmark{
myTableView.cellForRow(at: indexPath)?.accessoryType = UITableViewCellAccessoryType.none}
else{
myTableView.cellForRow(at: indexPath)?.accessoryType = UITableViewCellAccessoryType.checkmark
}
}
func downloadJsonWithTask() {
let url = NSURL(string: urlString)
var downloadTask = URLRequest(url: (url as URL?)!, cachePolicy: URLRequest.CachePolicy.reloadIgnoringCacheData, timeoutInterval: 20)
downloadTask.httpMethod = "GET"
URLSession.shared.dataTask(with: downloadTask, completionHandler: {(data, response, error) -> Void in
if let response = data {
if let jsonData = try? JSONSerialization.jsonObject(with: response, options: .allowFragments) as? [String:Any] {
if let dataArray = (jsonData as AnyObject).value(forKey: "users") as? [[String:Any]] {
for data in dataArray{
let newUser = Users(data: data)
self.userList.append(newUser)
print(jsonData!)
}
}
OperationQueue.main.addOperation({
for use in self.userList {
print(use.name ?? "")
}
self.myTableView.reloadData()
})
print(jsonData!)
}
}
}).resume()
}
}
The best way to achieve this to add a button instead of checkbox, set the images of checkbox for the selected and default state of button and in your "cellForRowAt" method do this:
In your "cellForRowAt" method:
let button = cell.viewWithTag(123) as? UIButton // 123 is tag that is defined in Stoyboard for this button/checkbox.
button.accessibilityHint = "\(indexPath.row)"
button.addTarget(self, action: "action:", forControlEvents:
UIControlEvents.TouchUpInside)
Paste this method any where in your ViewController:
func action(sender:UIButton!) {
let position = Int(sender.accessibilityHint) // This is the position that will help you to get the specific item from your list.
if (sender.selected == true)
{
sender.setBackgroundImage(UIImage(named: "box"), forState:
UIControlState.Normal)
sender.selected = false
// Remove from Datbase
}
else
{
sender.setBackgroundImage(UIImage(named: "checkBox"), forState:
UIControlState.Normal)
sender.selected = true
// Add into database.
}
}
This is a method of a button directly connected from Storyboard to ViewController. For your table view you have o follow these steps:
Get your button in "cellForIndexAt" method with Tag.
Set "accessibilityHint" of your button with "indexPath.row".
Add target to your button.
Get the position through "accessibilityHint" in your targeted method.
Use logic given above in your targeted method.
But if you don't want to go through all this process then simply add your item in databse in your "didSelectRowAt" method, but it will perform the action wherever user taps on the whole cell.

Swift 3 - Setting variable in TableViewController swift file depending on cell clicked

I'm trying to set the a string depending on which cell in a tableView is clicked. The BlueLineTableViewController is the one which should capture the user's click.
import UIKit
class BlueLineTableViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return bluelinestations.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "bluelinecell", for: indexPath)
let station = bluelinestations[indexPath.row]
cell.textLabel?.text = station.name
cell.imageView?.image = UIImage(named: station.image)
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let row = indexPath.row
if row == 0 {
BlueBelmontTableViewController().feed = "http://lapi.transitchicago.com/api/1.0/ttarrivals.aspx?key=mykey&mapid=40890&outputType=JSON"
}
if row == 1 {
BlueBelmontTableViewController().feed="http://lapi.transitchicago.com/api/1.0/ttarrivals.aspx?key=mykey&mapid=40820&outputType=JSON"
}
}
The BlueBelmontTableViewController's feed variable should change/be set to another url depending on which cell is clicked in the BlueLineTableViewController.
import UIKit
class BlueBelmontTableViewController: UITableViewController {
class Destinations {
var destination: String = ""
var time: String = ""
}
var feed = ""
var dataAvailable = false
var records = [Destinations]()
override func viewDidLoad() {
super.viewDidLoad()
parseData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
for r in records {
r.time = ""
r.destination = ""
}
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataAvailable ? records.count : 15
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if (dataAvailable) {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
let destinationRow = records[indexPath.row]
cell.textLabel?.text = destinationRow.destination
cell.detailTextLabel?.text = destinationRow.time
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "PlaceholderCell", for: indexPath)
return cell
}
}
func parseData() {
guard let feedURL = URL(string: feed) else {
return
}
let request = URLRequest(url: feedURL)
let task = URLSession.shared.dataTask(with: request) {(data, response, error) in
if error != nil
{
print("Error")
}
else {
if let content = data {
do {
let json = try JSONSerialization.jsonObject(with: content, options: []) as? [String:Any] ?? [:]
print(json)
if let ctattimetable = json["ctatt"] as? [String:Any] {
if let estArrivalTime = ctattimetable["eta"] as? [[String:Any]] {
for item in estArrivalTime{
if let headingTowards = item["destNm"] as? String,
let arrivalTime = item["arrT"] as? String {
let record = Destinations()
record.destination = headingTowards
record.time = arrivalTime
self.records.append(record)
}
self.dataAvailable = true
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
}
}
catch {
}
}
}
}
task.resume()
}
}
I've tried setting the url in the didSelectRowAt method depending on the indexPath.row as can be seen in BlueLineTableViewController, but it does not seem to do anything. Does anybody know how I would go about doing this?
Below is the Main.storyboard of this part of my project:
Your are not able to pass value because you are setting feed property to the completely new instance of BlueBelmontTableViewController not the one that is added in navigation stack using your segue that you have created from your UITableViewCell to BlueBelmontTableViewController.
What you need to do is override prepareForSegue in your BlueLineTableViewController to pass your value to BlueBelmontTableViewController.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let vc = segue.destination as! BlueBelmontTableViewController
if let indexPath = self.tableView.indexPathForSelectedRow {
if indexPath.row == 0 {
vc.feed = "http://lapi.transitchicago.com/api/1.0/ttarrivals.aspx?key=mykey&mapid=40890&outputType=JSON"
}
if indexPath.row == 1 {
vc.feed = "http://lapi.transitchicago.com/api/1.0/ttarrivals.aspx?key=mykey&mapid=40820&outputType=JSON"
}
}
}
instead of
BlueBelmontTableViewController().feed = "http://lapi.transitchicago.com/api/1.0/ttarrivals.aspx?key=mykey&mapid=40890&outputType=JSON"
use
self.feed = "http://lapi.transitchicago.com/api/1.0/ttarrivals.aspx?key=mykey&mapid=40890&outputType=JSON"
beacause BlueBelmontTableViewController() is initialing new instance of BlueBelmontTableViewController and you want to change the instance you already have so you should use self instead of creating new instance.

Resources