I have a tableviewController where I have a SearchBar. When I type in search box a cafeteria name then wrong image displays with the found name.
Here is my variables: Should I declare my dictionary array with title and image in another syntax format?
#IBOutlet weak var RetailTableView: UITableView! //our TableView
var resultSearch = UISearchController() //our UISearchbar
var menuItems = [["20/20 Cafe" : "20_20Cafe.jpg"],
["Au Bon Pain": "AuBonPain.jpg"]]
var menuItemsFiltered = [(String)]()
Here is my TableViewFunc:
internal func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCellWithIdentifier("RetailCell", forIndexPath: indexPath) as! ResidentialTableViewCell
let entry = menuItems[indexPath.row]
let image = UIImage(named: RetailImagesArray[indexPath.row])
if self.resultSearch.active
{
cell.RetailLabel.text = menuItemsFiltered[indexPath.row]
}
else
{
cell.RetailImage.image = image
cell.RetailLabel.text = entry.keys //Not Working
}
My search function is not working either
func updateSearchResultsForSearchController(searchController: UISearchController) {
self.FilteredArray.removeAll(keepCapacity: false)
let searchText = searchController.searchBar.text!.lowercaseString
FilteredArray = RetailPlacesArray.filter({$0.lowercaseString.rangeOfString(searchText) != nil})
RetailTableView.reloadData()
}
Please help, What I am doing wrong?
rangeOfString does not return nil if the string is not found, but an NSRange with the location NSNotFound.
Related
I have a custom view(tagListView) inside a custom tableview cell.
When I call addTag to cell.tagListView inside "cellForRowAt", it adds a tag for every cell.
How do I add tag only for that cell? I tried to keep a count so I only add tags to those which don't have tags. But it appears that this count is the same for all cells? I know this has something to do with reusable cell.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = topicListTableView.dequeueReusableCell(withIdentifier: "TopicListTableCell", for: indexPath)
if let myCell = cell as? TopicListTableCell {
if let model=topicListModel{
let t=model.topics[(indexPath.row)]
if let subject=t.subject{
let (tags,title)=util().getTag(subject: subject)
myCell.subject.text=title
if(myCell.tagCount==0){
myCell.tagList.addTags(tags)
myCell.tagCount+=1
}
}
myCell.content.text=t.content
myCell.authorTime.text=t.author
if let replynum=t.replies{
myCell.commentNum.text=String(replynum)
}
if let upvoteNum=t.score{
myCell.upVote.text=String(upvoteNum)
}
if indexPath.row%2==1{
myCell.background.backgroundColor=util().lightyellow
}else{
myCell.background.backgroundColor=util().darkeryellow
}
}
}
return cell
}
code for cell:
class TopicListTableCell: UITableViewCell{
#IBOutlet weak var content: UILabel!
#IBOutlet weak var upVote: UILabel!
#IBOutlet weak var commentNum: UILabel!
#IBOutlet weak var subject: UILabel!
#IBOutlet weak var authorTime: UILabel!
#IBOutlet weak var background: UIView!
#IBOutlet weak var tagList: TagListView!
var tagCount = 0
}
As dfd already stated, your approach isn't the right one because of the way UITableViews are working in iOS.
You should have one dataSource for your tableView (some array or dictionary) which contains all the information needed to present the cells the way you want.
So I would make a struct which contains all information I need to fill one cell and then make an array of these structs to fill all cells with the information they need.
Something like this:
// In your model:
struct TopicItem {
let topic: Topic
let tags: [Tag]
let title: String
}
var topicItems = [TopicItem]()
// This is function is only a draft, so you get a better idea what i mean
func updateTopicItems() {
topicItems.removeAll()
for topic in topics {
let tags: [Tag]
let title: String
if let subject = topic.subject {
// I would refactor this function, it is called getTag() but it returns tags and title. Kinda confusing
(tags,title) = util().getTag(subject: subject)
} else {
tags = [Tag]()
title = ""
}
topicItems.append(TopicItem(topic: topic, tags: tags, title: title))
}
}
I also have added some refactoring and comments which will hopefully help you to keep your code clean and maintainable in the future :)
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// cell can be force unwrapped, it will never be something different than a TopicListTableCell, unless you change your code
// and even then you want a crash, so you find the error fast (crash will only occur during development)
let cell = topicListTableView.dequeueReusableCell(withIdentifier: "TopicListTableCell", for: indexPath) as! TopicListTableCell
guard let model = topicListModel else { return cell } // If there is no model, there is nothing to do
let topicItem = model.topicItems[(indexPath.row)]
let t = topicItem.topic
cell.subject.text = topicItem.title
// You should replace the addTags() function with a setTags() function, otherwise you will get more and more tags every time you present the cell
cell.tagList.addTags(topicItem.tags)
cell.content.text = t.content
cell.authorTime.text = t.author
if let replynum = t.replies {
cell.commentNum.text = String(replynum)
} else {
// Because cells are getting reused, you need to always set all fields, otherwise you get cells with content from other cells
cell.commentNum.text = ""
}
if let upvoteNum = t.score {
cell.upVote.text = String(upvoteNum)
} else {
// Again always set all fields
cell.upVote.text = ""
}
if indexPath.row %2 == 1 {
cell.background.backgroundColor=util().lightyellow
} else {
cell.background.backgroundColor=util().darkeryellow
}
return cell
}
In the code below I'm populating my table with some data. The switches are off which they don't have to be. In the storyboard I defined it as On.
Cell:
var switchHandler: ((Bool)->Void)?
#IBAction func switchChanged(_ sender: UISwitch) {
self.switchHandler?(sender.isOn)
}
View controller:
var selectedCells = Set<IndexPath>()
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "SmsCell") as? SmsTableViewCell
cell?.PhonNumberLbl.text = data![indexPath.section].contacts[indexPath.row]?.phoneNumber
cell?.NameLbl.text = data![indexPath.section].contacts[indexPath.row]?.name
cell?.selectedTF.isOn = (data![indexPath.section].contacts[indexPath.row]?.selected)!
cell?.selectedTF.isOn = self.selectedCells.contains(indexPath)
cell?.switchHandler = { (switchState) in
if switchState {
self.selectedCells.insert(indexPath)
} else {
self.selectedCells.remove(indexPath)
}
}
return cell!
}
Model:
typealias smsModelList = [SmsModel]
struct SmsModel:Codable {
var unitNo:Int?
var unitPlaque:String?
var billText:String?
var contacts:[ContactsModel?]
}
typealias contactlistmodel = [ContactsModel]
struct ContactsModel:Codable
{
var id :Int?
var selected :Bool?
var phoneNumber : String?
var name : String?
}
Does anybody see somthing wrong which turns off the switch?
First of all as you force unwrap the cell anyway do it in the dequeue line to avoid the unnecessary amount of question marks and use the API to return a non-optional cell
let cell = tableView.dequeueReusableCell(withIdentifier: "SmsCell", for: indexPath) as! SmsTableViewCell
To fix your issue update the selected property of the ContactsModel struct directly and forget the extra selectedCells array. Further declare – at least – selected as non-optional, practically there is no maybe state. And declare also all data source arrays (data / contacts) as non-optional, cellForRow is called only if there is an item at the particular indexPath by default.
struct ContactsModel : Codable {
...
var selected : Bool
...
}
...
let cell = tableView.dequeueReusableCell(withIdentifier: "SmsCell", for: IndexPath) as! SmsTableViewCell
let contact = data[indexPath.section].contacts[indexPath.row]
cell.PhonNumberLbl.text = contact.phoneNumber
cell.NameLbl.text = contact.name
cell.selectedTF.isOn = contact.selected
cell.switchHandler = { [unowned self] switchState in
// as the structs are value types you have to specify the full reference to the data source array
self.data[indexPath.section].contacts[indexPath.row].selected = switchState
}
Consider to use classes rather than structs in this case then you can shorten the closure
cell.switchHandler = { switchState in
contact.selected = switchState
}
You use both
cell?.selectedTF.isOn = (data![indexPath.section].contacts[indexPath.row]?.selected)!
cell?.selectedTF.isOn = self.selectedCells.contains(indexPath)
so isOn property of the switch is controlled from 2 sides , so you have to decide which line that should be commnented , plus don't depend on storyboard prototype cell setup as because of cell reusing it' ll be changed , if you want to make them all on by default then change the var selectedCells to contain all possible indexPaths and comment the other one
Hey guys I´m in the process of learning swift right now and I try to program a Game. I want to show a list with items and different attributes to these Items.
So first I have User choice of where they can select either Food or Toys or other stuff coming in the future. Here I tried to only do one ViewController and change the stuff inside depending on the choice. Right now I have these Items in an Array from a Class.
They look like this:
class FoodData {
var name: String
var description = "Basic Food"
var owned = 0
var taste = 0
var price = 0
var water = 0
var image = "default.png"
init(name: String){
self.name=name
}
}
class ToyData {
var name: String
var description = "Basic Item"
var owned = 0
var price = 0
var joy = 0
var image = "default.png"
init(name: String){
self.name=name
}
}
I initialise these with:
var foodLoad=[FoodData]()
func loadFoodData(){
foodLoad.append(FoodData(name: "IceCream"))
foodLoad[0].description="Very Cold"
foodLoad[0].owned=10
}
Same style for the Toys. Now I have these two Classes in two Arrays called foodLoad[i] and toyLoad[I]
For the Table View I fill it with the protocols
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let shopCell = tableView.dequeueReusableCell(withIdentifier: shopCellIdentifier, for: indexPath) as! ShopCellStyle
shopCell.shopNameLabel?.text = shopData[indexPath.row].name
shopCell.shopImageView?.image = UIImage(named: shopData[indexPath.row].image)
shopCell.shopPriceLabel?.text = String(shopData[indexPath.row].price) + currency
return shopCell
So my Idea was to assign shopData with the User choice.
But if I assign shopData = foodLoad, I can't change that to toyLoad anymore. So maybe you can give me a hint of how to solve this the best way.
for your cellForRowAt indexPath:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let shopCell = tableView.dequeueReusableCell(withIdentifier: "shopCellIdentifier", for: indexPath) as! ItemCell
if selection == "food" {
shopCell.shopNameLabel?.text = foodLoad[indexPath.row].name
shopCell.shopImageView?.image = UIImage(named:
foodLoad[indexPath.row].image)
shopCell.shopPriceLabel?.text = String(foodLoad[indexPath.row].price) + currency
}
else if selection == "toys" {
shopCell.shopNameLabel?.text = toyLoad[indexPath.row].name
shopCell.shopImageView?.image = UIImage(named:
toyLoad[indexPath.row].image)
shopCell.shopPriceLabel?.text = String(toyLoad[indexPath.row].price) + currency
}
return shopCell
}
You'd also want the numberOfRowsInSection UITableView function. Call tableview.reloadData() when the user changes the type selection.
I ran into an issue that I'm not sure how to go about solving. I have a Cell object where I create some IBOutlets that I want to display inside my UITableView to my screen like so:
class EventCell: UITableViewCell, CellDelegate{
#IBOutlet var eventName: UILabel!
#IBOutlet var eventLocation: UILabel!
#IBOutlet var attendeesImages: [UIImageView]!
}
I also have another function where I try to set the cell contents of that cell like so:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
//Dequeue a "reusable" cell
let cell = tableView.dequeueReusableCellWithIdentifier(eventCellIdentifier) as! EventCell
setCellContents(cell, indexPath: indexPath)
return cell
}
//Set contents of Event Cell.. self.events is a global array
//which have information that EventCell objects need to display
func setCellContents(cell:EventCell, indexPath: NSIndexPath!){
let item = self.events[indexPath.section]
var count = 0
cell.eventName.text = item.eventName()
cell.eventLocation.text = item.eventLocation()
//Attempt to set the cell.attendeesImage values
for value in item.attendeesImages() {
cell.attendeesImages[count].image = value
cell.attendeesImages[count].clipsToBounds = true
cell.attendeesImages[count].layer.cornerRadius = cell.attendeesImage[count].frame.size.width / 2
count++
}
}
The issue is, when I try to set the values of cell.attendeesImages, I run into an issue saying fatal error: Array index out of range. The issue is because I am accessing an index that doesn't exist in cell.attendeesImages. Is there any way to allocate or set the size of cell.attendeesImages before I assign its contents? Or is there simply a better way to assign the images within the cell.attendeesImages? I also tried to set cell.attendeesImages as an array of UIImage and setting cell.attendeesImages = item.attendeesImages but if it is as an UIImage, I can't seem to display the images onto the screen whereas the UIImageView will allow me to do so. Any help would be appreciated. Thanks!
Replace
for value in item.attendeesImages() {
cell.attendeesImages[count].image = value
cell.attendeesImages[count].clipsToBounds = true
cell.attendeesImages[count].layer.cornerRadius = cell.attendeesImage[count].frame.size.width / 2
count++
}
With
for (count, value) in enumerate(item.attendeesImages()) {
var workingImage : UIImageView! = nil
if count >= cell.attendeesImages.count {
workingImage = UIImageView(image : value)
cell.addSubview(workingImage)
cell.attendeesImages.append(workingImage)
//TODO: Layout image based on count
} else {
workingImage = cell.attendeesImages[count]
}
workingImage.clipsToBounds = true
workingImage.layer.cornerRadius = workingImage.frame.size.width / 2
}
//Loop through remaining images from cell-reuse and hide them if they exist
for var index = item.attendeesImages().count; index < cell.attendeesImages.count; ++index {
cell.attendeesImages[index].image = nil
}
I am trying to load data from a structure to table view cell, I created a custom cell with three label in it. I have three text field in the view controller and a add button I want that when I fill these three text field and press add it will store these three values in a structure and reload the data of table view. Structure is in other file.
Here is code for structure in DataMaster.swift
struct jobData
{
var CompanyName:Array<String> = ["ram"]
var job:Array<String> = ["shyam"]
var desc:Array<String> = ["dfdf"]
}
Code for addButton function
#IBAction func addButtonTapped(sender: AnyObject) {
var company = txtCompName.text
var job = txtJob.text
var description = txtDesc.text
data.CompanyName.append(company)
data.desc.append(description)
data.job.append(job)
self.jobTableView.reloadData()
print(data.CompanyName)
txtCompName.resignFirstResponder()
txtJob.resignFirstResponder()
txtDesc.resignFirstResponder()
}
The problem is in this code
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath:indexPath) as jobTableViewCell
cell.compLabel.text = data.CompanyName[indexPath.row]
cell.jobLabel.text = data.job[indexPath.row]
cell.descLabel.text = data.desc[indexPath.row]
return cell
}
when it reaches to this code to load data in table it crashes
Thread 1:EXC_BREAKPOINT(code=EXC_I386_BPT,subcode=0x0)
Here below is code.
struct jobData
{
var CompanyName:String!
var job:String!
var desc:String!
}
Take an array as var datas = [jobData]()
Now in Action method
#IBAction func addButtonTapped(sender: AnyObject) {
var company = txtCompName.text
var job = txtJob.text
var description = txtDesc.text
let dataObject = jobData(company: company, job: job, desc: description)
datas.append(dataObject)
self.jobTableView.reloadData()
txtCompName.resignFirstResponder()
txtJob.resignFirstResponder()
txtDesc.resignFirstResponder()
}
Now in cellForRowAtIndex method
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath:indexPath) as! jobTableViewCell
let data = datas[indexPath.row]
if let companyName = data.CompanyName {
cell.compLabel.text = companyName
}
if let job = data.job {
cell.jobLabel.text = job
}
if let descr = data.desc {
cell.descLabel.text = descr
}
return cell
}
in numberofRowsInSection method return datas.count
Check why data.CompanyName is empty and make sure all text field will have text.