I wrote an app that it have a UITableView in UIViewController and here is my codes:
class CategorySelectViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var melliSubCategories = [String]()
var mazhabiSubCategories = [String]()
var sayerSubCategories = [String]()
#IBOutlet weak var melliButton: UIButton!
#IBOutlet weak var sayerButton: UIButton!
#IBOutlet weak var mazhabiButton: UIButton!
#IBAction func melliButtonClicked(_ sender: UIButton) {
categorySelected = 6
melliButton.isHighlighted = true
mazhabiButton.isHighlighted = false
sayerButton.isHighlighted = false
categoryTableView.reloadData()
}
#IBAction func sayerButtonClicked(_ sender: UIButton) {
categorySelected = 5
melliButton.isHighlighted = false
mazhabiButton.isHighlighted = false
sayerButton.isHighlighted = true
categoryTableView.reloadData()
}
#IBAction func mazhabiButtonClicked(_ sender: UIButton) {
categorySelected = 4
melliButton.isHighlighted = false
mazhabiButton.isHighlighted = true
sayerButton.isHighlighted = false
categoryTableView.reloadData()
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
categoryTableView.dataSource = self
categoryTableView.delegate = self
categoryTableView.register(CategorySelectTableViewCell.self, forCellReuseIdentifier: "Cell")
melliSubCategories = DataBaseManager.shared.subCategories(6)
mazhabiSubCategories = DataBaseManager.shared.subCategories(4)
sayerSubCategories = DataBaseManager.shared.subCategories(5)
print(melliSubCategories)
print("/////////////////")
print(mazhabiSubCategories)
print("/////////////////")
print(sayerSubCategories)
print("/////////////////")
}
#IBOutlet weak var categoryTableView: UITableView!
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch categorySelected {
case 4: //mazhabi
return mazhabiSubCategories.count
case 5: //sayer
return sayerSubCategories.count
case 6: //melli
return melliSubCategories.count
default:
return melliSubCategories.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath as IndexPath) as! CategorySelectTableViewCell
cell.label?.text = melliSubCategories[indexPath.row]
return cell
}
And I create a class named CategorySelectTableViewCell for cells in table view that they have an image and a label.
In code I fill the arrays by database and I want to show them in the table view but the tableview doesn't show anything.
the screenshot : my storyboard, demo
You are saying:
cell.label?.text = melliSubCategories[indexPath.row]
It is impossible for this to work. For it to work, your custom cell type CategorySelectTableViewCell would need to be in a nib with a label outlet. But then that nib is either in a storyboard or a xib file. But you are also saying
categoryTableView.register(CategorySelectTableViewCell.self, ...
That line prevents the cell from coming from the xib or the storyboard. So the outlet cannot work and the cell will remain empty.
Please check array count is Zero or not in below numberOfRowsInSection method.
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
Also, cross-check cell identifier is correct or not.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell : CategorySelectTableViewCell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath as IndexPath) as! CategorySelectTableViewCell
cell.label?.text = melliSubCategories[indexPath.row]
return cell
}
Reload tableview in ViewDidLoad method:
categoryTableView.reloadData()
override func viewDidLoad()
{
super.viewDidLoad()
// Do any additional setup after loading the view.
categoryTableView.dataSource = self
categoryTableView.delegate = self
categoryTableView.register(CategorySelectTableViewCell.self, forCellReuseIdentifier: "Cell")
melliSubCategories = DataBaseManager.shared.subCategories(6)
mazhabiSubCategories = DataBaseManager.shared.subCategories(4)
sayerSubCategories = DataBaseManager.shared.subCategories(5)
print(melliSubCategories)
print("/////////////////")
print(mazhabiSubCategories)
print("/////////////////")
print(sayerSubCategories)
print("/////////////////")
categoryTableView.reloadData()
}
Related
so I want to make this simple wishlist feature for when the user tapped the "heart" button it will add that data from view to wishlist view. just like this :
so when the user tapped that heart button, that movie will show in this wishlist view like this :
now, my question is how do I notify my wishlistVc so that it knows there's a new "wishlist" that the user tapped from the movie list. I have an idea that I should use a delegate, but still, I can't figure out how to implement a delegate in this case.
and I use "var movieList" to store all the data in HomeVc, and my idea is when the user tapped that heart button in tableview, that data that user tapped with will move into my "let wishlist", so i can populate it on my wishlistVC ( but I don't know how to do this so I need help)
so far this is my code :
class DefaultTableViewCell: UITableViewCell {
#IBOutlet weak var moviePosterImage: UIImageView!
#IBOutlet weak var movieTitleLabel: UILabel!
#IBOutlet weak var wishlistButton: UIButton!
var indexPath: IndexPath!
var delegate: DefaultTableViewDelegate?
var wishlistFlag:Bool = false
override func layoutSubviews() {
super.layoutSubviews()
wishlistButton.titleLabel?.text = ""
wishlistButton.addTarget(self, action: #selector(wishlistTapped(_:)), for: .valueChanged)
}
#IBAction func wishlistTapped(_ sender: UIButton) {
wishlistFlag = !wishlistFlag
delegate?.wishlistTrigger(row: indexPath.row)
if wishlistFlag == true {
wishlistButton.setImage(UIImage(named: "heart_fill"), for: .normal)
}else if wishlistFlag == false {
wishlistButton.setImage(UIImage(named: "heart"), for: .normal)
}
}
}
HomeVc (the vc that shows the movie list):
var movieList : [Movie] = []
extension HomeVC: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return movieList.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let data = movieList[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "DefaultTableViewCell", for: indexPath) as! DefaultTableViewCell
cell.indexPath = indexPath
cell.movieTitleLabel.text = data.title
cell.moviePosterImage.sd_setImage(with: data.imageUrl)
cell.delegate = self
return cell
}
}
protocol DefaultTableViewDelegate {
func wishlistTrigger(row: Int)
}
this is my wishlistVc:
let wishlist : [Movie] = []
extension WishlistVc: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return wishlist.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let data = wishlist[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "DefaultTableViewCell", for: indexPath) as! DefaultTableViewCell
cell.movieTitleLabel.text = data.title
cell.moviePosterImage.sd_setImage(with: data.imageUrl)
cell.wishlistButton.titleLabel?.text = ""
cell.indexPath = indexPath
return cell
}
}
I've been stuck for 2 whole days now I still don't know how to figure this out. I appreciate anyone that can help me. Thanks
Implement func like:
func wishlistTrigger(row: Int) {
self.myWishlistedItem.append(self.movieList[row]) //Add that wishlisted item in array
self.tableView.reloadData() //Now reload Table
}
This question already has an answer here:
Swift Using a UISlider in a UITableViewCell the right way?
(1 answer)
Closed 2 years ago.
My program is creating slider in dynamic cell. I can't just create IBAction using ctrl+"drag and drop" on slider because it is in cell, not in viewController. How can I create this IBAction in ViewController?
class SecondViewController: UIViewController {
var gradesNumber: Int?
var gradeArray: [Grade] = []
#IBOutlet weak var topLabel: UILabel!
#IBOutlet weak var gradesTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
gradesTableView.dataSource = self
for i in 1...gradesNumber! {
gradeArray.append(Grade(sliderValue: 5, title: "Grade \(i)", grade: "5"))
}
gradesTableView.register(UINib(nibName: "TableViewCell", bundle: nil), forCellReuseIdentifier: "ReusableCell")
}
#IBAction func calculateButton(_ sender: UIButton) {
var avg = 0.0
for i in gradeArray {
avg += Double(i.grade)!
}
topLabel.text = "Avg: \(avg/Double(gradesNumber!))"
}
}
extension SecondViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return gradesNumber ?? 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ReusableCell", for: indexPath) as! TableViewCell
cell.gradeSlider.tag = indexPath.row
cell.gradeSlider.setValue(5, animated: true)
let slider: Grade = gradeArray[indexPath.row]
cell.gradeValue.text = slider.grade
cell.gradeNumber.text = slider.title
return cell
}
}
As Lucas says in his comment, you can create a custom subclass of UITableViewCell and attach the action to the cell class. Then you'd forward the message from the cell to the owning table view. (You'd probably need a delegate property on the cell class so you know who to forward the action to.)
Alternately, you could configure the IBAction in your cellForRowAt method:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ReusableCell", for: indexPath) as! TableViewCell
//other config code
//Make sure the slider doesn't already have the action attached.
if cell.gradeSlider.actions(forTarget: nil, forControlEvent: .valueChanged) == nil {
cell.gradeSlider.addTarget(self, action: #selector(actionSelector(:)), controlEvents: .valueChanged]
}
return cell
}
I am working on the iOS application with Swift 4. In that project I have requirement like, I have to create controls dynamically along with the proper alignment.
For example, I have a button when I click on that button I am hitting the service from that I am getting json data which contains 4 objects. Based on that I have to create controls dynamically and dynamic alignment also should do. I tried lot of examples and tutorials. I didn’t find any solution.
You can use UITableView for that and here is example:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var tableview: UITableView!
var nameArr :[String] = []
override func viewDidLoad() {
super.viewDidLoad()
tableview.delegate = self
tableview.dataSource = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func four_btn(_ sender: Any) {
nameArr.removeAll()
let nameData = ["First Name","Middle Name","Last Name","DOB"]
nameArr += nameData
tableview.reloadData()
}
#IBAction func eight_btn(_ sender: Any) {
nameArr.removeAll()
let nameData = ["Salutation","First Name","Middle Name","Last Name","DOB","Gender","Mobile","Email"]
nameArr += nameData
tableview.reloadData()
}
}
extension ViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return nameArr.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! tableviewCells
cell.nameLabel.text = nameArr[indexPath.row]
return cell
}
}
class tableviewCells : UITableViewCell{
#IBOutlet weak var nameLabel: UILabel!
}
You can use UITableView for the same
Your scenario is like, it may possible that one user having 5 records however another may have 10 or 12 records means you've to work dynamically
if there are 2 buttons which calls 2 different APIs then just manage 2 different array like this
var arr1 = NSArray()
var arr2 = NSArray()
var isAPI1Called = Bool()
save response of both apis in different array
then just manage flag on button tap and in suitable view like this
#IBAction func btn1(_ sender: Any) {
isAPI1Called = true
self.API1Called()
}
#IBAction func btn2(_ sender: Any) {
isAPI1Called = false
self.API1Called()
}
Now use flag in UITableview Delegate like this
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if isAPI1Called
{
return arr1.count
}
else
{
return arr2.count
}
}
Load UITableviewCell as per your requirement if UI changed
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if isAPI1Called
{
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell1", for: indexPath) as! UITableviewCell
//Do your required stuff here
return cell
}
else
{
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell1", for: indexPath) as! UITableviewCell
//Do your required stuff here
return cell
}
}
Hope it will help you
Comment if not get any point
I've searched for a solutions on this issue but none seem to work for my use case.
I have a table inside a viewcontroller and the issue I am facing is that when scrolling the UISwitch state is reset to OFF. I understand table cells are reused, but how do I implement a solution that will restore the state of UISwitch when a user scrolls based on my code below
import UIKit
class StirrViewController: UIViewController, UITableViewDelegate, UITableViewDataSource{
#IBOutlet weak var mylabel: UILabel!
var myString = String()
#IBAction func stirrBtn(_ sender: AnyObject) {
}
var timeSelected = String()
var selectedTimeArr = [String]()
override func viewDidLoad() {
super.viewDidLoad()
mylabel.text = myString
self.timeSelected = myString
}
func switchChanged(_ sender : UISwitch!){
print("table row switch Changed \(sender.tag)")
print("The switch is \(sender.isOn ? "ON" : "OFF")")
let kValue = (sender.tag + 1)
let keyValue = String(kValue)
if sender.isOn {
recipeSettings.boolStirrSwitch[keyValue] = true
recipeSettings.switchedOnArr.append(keyValue)
} else {
recipeSettings.boolStirrSwitch[keyValue] = false
}
}
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
let stringNum = Int(self.timeSelected)
recipeSettings.recipeTimeSet2 = stringNum!
return(stringNum)!
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
var cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! UITableViewCell
//here is programatically switch make to the table view
let switchView = UISwitch(frame: .zero)
switchView.setOn(false, animated: true)
switchView.tag = indexPath.row // for detect which row switch Changed
switchView.addTarget(self, action: #selector(self.switchChanged(_:)), for: .valueChanged)
cell.accessoryView = switchView
// Process data displayed in rows(minutes)
let endTime = Int(self.timeSelected)
let startTime = Int(1)
// Recipe time array
let timeArray: [Int] = Array(startTime...endTime!)
let stringTimeArr = timeArray.map{String($0)}
// Save time array to global variable
recipeSettings.recipeTimeSetArr = stringTimeArr
// Create a boolean Array to hold all default false booleans
let defBool: Bool = false
var defBoolArr: [Bool] = []
// Fill the array with the defaults boolean
for _ in 0..<stringTimeArr.count{defBoolArr.append(defBool)}
// Map the array to global dictionary containing the Time in an array and default "false" value
for i in 0..<stringTimeArr.count {
recipeSettings.boolStirrSwitch[stringTimeArr[i]] = defBoolArr[i]
}
// Add the minutes to cell table
cell.textLabel?.text = stringTimeArr[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
As you can see in my code I do save the state of each UI switch in a global variable dictionary. How can I solve the issue of UISwitch changing states based on this code? All help is appreciated. Thanks in advance
var switchState = [String : Bool]()
your recipeSettings.boolStirrSwitch should be decleard like that.
As you are using timeSelected as numberOfRowsInSection as showing
your cell.textLabel from that so you don't need extra stringTimeArr
for that.
All the processing you do in cellForRowAt it will happen again and
again table cells are reused so for setting up data do it in another
function then reload TableView.
Solution for your problem should be look like that.
import UIKit
class StirrViewController: UIViewController, UITableViewDelegate, UITableViewDataSource{
//make tableView IBOutlet for reloading data
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var mylabel: UILabel!
var myString = String()
#IBAction func stirrBtn(_ sender: AnyObject) {
}
var timeSelected = String()
var selectedTimeArr = [String]()
override func viewDidLoad() {
super.viewDidLoad()
mylabel.text = myString
self.timeSelected = myString
self.setdefaultSwitchState()
}
//recipeSettings.boolStirrSwitch should be decleard like that
var switchState = [String : Bool]()
//setDeaultSwitchState
func setdefaultSwitchState(){
if let timeSelected = Int(self.timeSelected){
for value in 0..<timeSelected{
switchState["\(value)"] = false
//or
//recipeSettings.boolStirrSwitch["\(value)"] = false
}
}
self.tableView.reloadData()
}
#objc func switchChanged(_ sender : UISwitch!){
print("table row switch Changed \(sender.tag)")
print("The switch is \(sender.isOn ? "ON" : "OFF")")
let kValue = (sender.tag + 1)
let keyValue = String(kValue)
if sender.isOn {
switchState[keyValue] = true
} else {
switchState[keyValue] = false
}
}
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
let stringNum = Int(self.timeSelected)
recipeSettings.recipeTimeSet2 = stringNum!
return(stringNum)!
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
var cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! UITableViewCell
//here is programatically switch make to the table view
let switchView = UISwitch(frame: .zero)
switchView.setOn(false, animated: true)
switchView.tag = indexPath.row // for detect which row switch Changed
switchView.addTarget(self, action: #selector(self.switchChanged(_:)), for: .valueChanged)
cell.accessoryView = switchView
cell.textLabel?.text = "\(indexPath.row + 1)"
if let switchState = switchState["\(indexPath.row)"] {
if switchState{
switchView.isOn = true
}else{
switchView.isOn = false
}
}else{
switchView.isOn = false
}
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
I have a UITableView that updates when I scroll up, but it does not update when I scroll down. Furthermore, when it does update it occasionally seems to "skip" a cell and update the next one.
There are 6 total cells that should populate
I've created the UITableView in the storyboard, set my constraints for both the hashLabel and the creditLabel in storyboard
Here is the image of the initial TableView:
And upon scrolling up, when updated properly:
...and when scrolling up "misses" a cell:
and of course, the class:
class HashtagController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var model:ModelData!
var currentCell: UITableViewCell!
#IBOutlet var hashtagTableView: UITableView!
let basicCellIdentifier = "CustomCells"
override func viewDidLoad() {
super.viewDidLoad()
model = (self.tabBarController as CaptionTabBarController).model
hashtagTableView.delegate = self
hashtagTableView.dataSource = self
self.navigationController?.navigationBar.titleTextAttributes = [ NSFontAttributeName: UIFont(name: "CherrySwash-Regular", size: 25)!, NSForegroundColorAttributeName: UIColor(red:27.0/255, green: 145.0/255, blue: 114.0/255, alpha: 1.0)]
configureTableView()
hashtagTableView.reloadData()
}
func configureTableView() {
hashtagTableView.rowHeight = UITableViewAutomaticDimension
hashtagTableView.estimatedRowHeight = 160.0
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
//deselectAllRows()
hashtagTableView.reloadData()
}
override func viewDidAppear(animated: Bool) {
hashtagTableView.reloadData()
}
func deselectAllRows() {
if let selectedRows = hashtagTableView.indexPathsForSelectedRows() as? [NSIndexPath] {
for indexPath in selectedRows {
hashtagTableView.deselectRowAtIndexPath(indexPath, animated: false)
}
}
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return model.quoteItems.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
return customCellAtIndexPath(indexPath)
}
func customCellAtIndexPath(indexPath:NSIndexPath) -> CustomCells {
var cell = hashtagTableView.dequeueReusableCellWithIdentifier(basicCellIdentifier) as CustomCells
setTitleForCell(cell, indexPath: indexPath)
setSubtitleForCell(cell, indexPath: indexPath)
return cell
}
func setTitleForCell(cell:CustomCells, indexPath:NSIndexPath) {
let item = Array(Array(model.quoteItems.values)[indexPath.row])[0] as? String
cell.hashLabel.text = item
}
func setSubtitleForCell(cell:CustomCells, indexPath:NSIndexPath) {
let item = Array(model.quoteItems.keys)[indexPath.row]
cell.creditLabel.text = item
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
/*currentCell = tableView.cellForRowAtIndexPath(indexPath) as UITableViewCell!
var currentLabel = currentCell.textLabel?.text
var currentAuthor = currentCell.detailTextLabel?.text
model.quote = currentLabel!
model.author = currentAuthor!*/
}
}
class CustomCells: UITableViewCell {
#IBOutlet var hashLabel: UILabel!
#IBOutlet var creditLabel: UILabel!
}
As it turns out, the issue had to do with my estimatedRowHeight. In this case the row height was too large and it was effecting the way the table cells were being constructed.
So in the end I changed hashtagTableView.estimatedRowHeight = 160.0 to hashtagTableView.estimatedRowHeight = 80.0 and everything worked just fine.