I've written a program where a user can take pictures and track information about their miniature collections and everything works as intended. Now I'm looking to add some different functionality for the tableview, and I'm not quite sure what would be the best way to go about it. At present when the user adds a model, it appends to a single dictionary of dictionaries then displays it in the tableview in the order it was appended. I would like to sort or separate the data into separate sections based on what codex the model is from.
Whether it would be better to generate separate sections programmatically or use an index, I'm not sure. But in either case, I am at a complete loss of how to accomplish this.
Here is the tableview code I currently have, in case it helps
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return models.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Dequeue reusable Cell
let cell = tableView.dequeueReusableCell(withIdentifier: "modelCell", for: indexPath) as! modelCellTableViewCell
// Fetch Item
let model = models[indexPath.row]
// Configure Table View Cell
cell.modelNickname?.text = model.modelNickname
cell.modelNickname.textColor = UIColor(red:0.24, green:0.31, blue:0.35, alpha:1.0)
cell.modelName?.text = model.modelName
cell.codexName?.text = model.codexName
cell.modelOption1?.text = model.modelOption1
cell.modelOption2?.text = model.modelOption2
cell.modelOption3?.text = model.modelOption3
cell.modelOption4?.text = model.modelOption4
let fileManager = FileManager.default
let imageName = model.codexName + model.modelName + model.modelOption1
let imagePath = (NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString).appendingPathComponent(imageName)
if fileManager.fileExists(atPath: imagePath){
cell.modelImage.image = UIImage(contentsOfFile: imagePath)
}else{
print("No Image found")
}
return cell
}
Any help/suggestions you could offer would be a big help.
Related
My JsonData -
let imagestring : String? = (myData as AnyObject).value(forKey: "Post_mid_image") as? String
if imagestring != nil {
let imageTrueString = "https://www.zdoof.com/" + imagestring!
self.imageStringArray.append(imageTrueString )
}
if let NameString = (myData as AnyObject).value(forKey: "Name") as? String {
self.nameStringArray.append(NameString)
}
When i am trying to set it to the table view cell
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.postLableArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "reUse", for: indexPath)
let myImage = cell.viewWithTag(30) as! UIImageView
myImage.clipsToBounds = true
if indexPath.row < imageStringArray.count {
if let myImageString = imageStringArray[indexPath.row] as? String {
let ImageUrl = URL.init(string: myImageString)
myImage.kf.setImage(with: ImageUrl)
}
}
return cell
}
The image is repeating in every cell . Why it is happening ? Please help
As per the response you have given, you can show the image like below:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let dict = myData as AnyObject
let cell = tableView.dequeueReusableCell(withIdentifier: "reUse", for: indexPath)
let myImage = cell.viewWithTag(30) as! UIImageView
if dict["Post_mid_image"] != nil {
let imageUrl = URL.init(string: strImageUrl)
myImage.kf.setImage(with: imageUrl)
} else {
//Set placeholder image showing no image available
}
return cell
}
Problem is with cell re-usablity of table view here ,you have to handle it , you can have SDWebImage library for loading images in cell or you can have your own image cache which caches images with key/values , key as in image url , so dynamically checking image url for item at indexpath and load cached image with that image url as key.
Hope it helps!!
This is happening because of using tableView.dequeueReusableCell(withIdentifier: "reUse", for: indexPath).
Basically whenever you use dequeueReusableCell(withIdentifier:,For:), it will use the same cell for all of data. It means the total number of cell which are on screen are only going to load, for all other cell, it will use same cell with different value.
now consider a scenario that you are having 500 cells in tableview, but we can manage at most 10-15 cells in display, so for all other cells it will use same cells just modify the value of cell.
so what you can do here is, whenever you use if statement, don't forgot to add else too.
because for one scenario if cell's background is set to red, than we need to add else for another scenario, as cells are just repeated.
I have a view and a tableview which it shows file list. When user click button in view then I open tableview. User click the file list row then I started to download file from my server and when download start I changed the file name like "11111", when download finish change file name like "22222" (now change the file name later I will put progress view)
In first run everything is working correctly. Download and change name working. But in tableview when I come back to view and go to tableview again, download is working but not change then file name in tableview.
What is wrong in my code and how can I show text value?
PS: When print the text value in tableview before return cell, it shows correct text.
My codes:
func setProgressValue(_ dict : NSDictionary){
//Download progress value
let cellProgressValue = dict.value(forKey: "value") as! Float
if cellProgressValue < 1.0{
fileList[cellNum].title = "111111"
updateTable()
}
else{
fileList[cellNum].title = “22222”
updateTable()
}
}
func updateTable(){
self.tableView.reloadData()
}
func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return fileList.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let identifier = "fileCell"
let cell = tableView.dequeueReusableCell(withIdentifier: identifier, for: indexPath)
cell.textLabel?.text = self.fileList[indexPath.row].title
return cell
}
//Call in viewdidload
func getFiles(){
for index in songList{
let urlString = "https://www.myapis.com/data/files"
Alamofire.request(urlString).validate().responseJSON { response in
let result = JSON(response.result.value)
if let items = result["items"].array {
for item in items {
//print(item)
let id = item["fileId"].stringValue
let title = ["fileName"].stringValue
let file = Files()
file.id = id
file.title = title
self.fileList.append(song)
}
self.tableView.reloadData()
}
}
}
}
I want to create a table like this in which on there will be two cells in one section. In first cell the profile image comes and description in the second cell. what I have tried so far is I have set Prototypes to 2 and give each prototype a unique identifier and also created two classes for two prototypes. But The problem is it is showing two rows but both rows have same data.
var profileImage = ["angelina","kevin"]
var userName = ["Angelina Jolie","Vasiliy Pupkin"]
var requestTitle = ["I have a Wordpress website and I am looking for someone to create a landing page with links and a cart to link up.","second description"]
var date = ["Feb 03, 16","Feb 03, 16"]
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return profileImage.count
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return profileImage.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if(indexPath.row==0){
let cell = tableView.dequeueReusableCellWithIdentifier("firstCustomCell", forIndexPath: indexPath) as! FirstProductRequestTableViewCell
cell.userNameLabel.text = userName[indexPath.row]
cell.profileImage.image = UIImage(named: profileImage[indexPath.row])
return cell
}else{
let cell = tableView.dequeueReusableCellWithIdentifier("secondCustomCell", forIndexPath: indexPath) as! SecondProductRequestTableViewCell
cell.requestTitleTxtView.text = requestTitle[indexPath.row]
return cell
}
}
Here is solution for your question.
You need to use UITableViewSecionHeaderView in this case because i think in your scenario you have multiple descriptions against a profile so put the SecionHeader which contain the information of profile and cells contain the description.
But if you want to Repeat the whole cell then you only need to make a CustomCell which contain profile information and description with a line separator. You can make a line separator with an Image or using UIView of height 1.0 and colour lightgray.
Based on your description, the number of sections returned is correct, but you should return 2 for the number of rows in each section and when configuring the cells you should be using indexPath.section where you're currently using the row to choose the data to add to the cell (but keep using the row to choose which type of cell to return).
So, the section is used to choose which person you're showing details about and the row is used to choose which piece of information to show:
var profileImage = ["angelina","kevin"]
var userName = ["Angelina Jolie","Vasiliy Pupkin"]
var requestTitle = ["I have a Wordpress website and I am looking for someone to create a landing page with links and a cart to link up.","second description"]
var date = ["Feb 03, 16","Feb 03, 16"]
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return profileImage.count
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 2
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if(indexPath.row == 0) {
let cell = tableView.dequeueReusableCellWithIdentifier("firstCustomCell", forIndexPath: indexPath) as! FirstProductRequestTableViewCell
cell.userNameLabel.text = userName[indexPath.row]
cell.profileImage.image = UIImage(named: profileImage[indexPath.section])
return cell
} else {
let cell = tableView.dequeueReusableCellWithIdentifier("secondCustomCell", forIndexPath: indexPath) as! SecondProductRequestTableViewCell
cell.requestTitleTxtView.text = requestTitle[indexPath.section]
return cell
}
}
I'm trying to create a simple music player that lists all the songs on my device in a UITableView split into alphabetic sections (like the Apple Music Player).
There are two areas I can't figure out:
Getting the number of songs per section so that I can create the
correct number of rows in my table for each section
Accessing the items in each section to populate the section cells
My code is already pretty long so I'll put the pertinent info here:
I get the list of songs like this:
let songsQry:MPMediaQuery = MPMediaQuery.songsQuery()
This is where I'm unsure:
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
//How can I get the number of songs per Section to return??
// this does not work:
return songsQry.itemSections![0].count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:UITableViewCell = UITableViewCell(style: UITableViewCellStyle.Subtitle, reuseIdentifier: "cell")
//How do I get the song for the Section and Row??
cell.textLabel?.text = ????
return cell
}
I'm going to answer my own question.
I've posted similar questions here on SO and everyone replying said this couldn't be done and that additional Arrays were needed plus custom sort routines. That's simply not true.
Note however, that in my example code I show how get the same kind of results for an Album Query. The code for an Artist query is almost identical.
This code uses the return from an Album Query to:
1) Build a TableView index
2) Add Albums by Title (using Apple's sorting) to table Sections
3) Start playing an album when selected
Here's the Swift code to prove it:
// Set up a basic Albums query
let qryAlbums = MPMediaQuery.albumsQuery()
qryAlbums.groupingType = MPMediaGrouping.Album
// This chunk handles the TableView Index
//create index title
func sectionIndexTitlesForTableView(tableView: UITableView) -> [String]? {
let sectionIndexTitles = qryAlbums.itemSections!.map { $0.title }
return sectionIndexTitles
}
func tableView(tableView: UITableView, sectionForSectionIndexTitle title: String, atIndex index: Int) -> Int {
return index
}
// This chunk sets up the table Sections and Headers
//tableview
func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return (qryAlbums.itemSections![section].title)
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return (qryAlbums.itemSections?.count)!
}
// Get the number of rows per Section - YES SECTIONS EXIST WITHIN QUERIES
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return qryAlbums.collectionSections![section].range.length
}
// Set the cell in the table
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:UITableViewCell = UITableViewCell(style: UITableViewCellStyle.Subtitle, reuseIdentifier: "cell")
// i'm only posting the pertinent Album code here.
// you'll need to set the cell details yourself.
let currLoc = qryAlbums.collectionSections![indexPath.section].range.location
let rowItem = qryAlbums.collections![indexPath.row + currLoc]
//Main text is Album name
cell.textLabel!.text = rowItem.items[0].albumTitle
// Detail text is Album artist
cell.detailTextLabel!.text = rowItem.items[0].albumArtist!
// Or number of songs from the current album if you prefer
//cell.detailTextLabel!.text = String(rowItem.items.count) + " songs"
// Add the album artwork
var artWork = rowItem.representativeItem?.artwork
let tableImageSize = CGSize(width: 10, height: 10) //doesn't matter - gets resized below
let cellImg: UIImageView = UIImageView(frame: CGRectMake(0, 5, myRowHeight-10, myRowHeight-10))
cellImg.image = artWork?.imageWithSize(tableImageSize)
cell.addSubview(cellImg)
return cell
}
// When a user selects a table row, start playing the album
// This assumes the myMP has been properly declared as a MediaPlayer
// elsewhere in code !!!
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let currLoc = qryAlbums.collectionSections![indexPath.section].range.location
myMP.setQueueWithItemCollection(qryAlbums.collections![indexPath.row + currLoc])
myMP.play()
}
Also, here's a few helpful notes:
1) List all the Albums from the query:
for album in allCollections!{
print("---------------")
print("albumTitle \(album.items[0].albumTitle)")
print("albumTitle \(album.representativeItem?.albumTitle)")
print("albumTitle \(album.representativeItem?.valueForProperty(MPMediaItemPropertyAlbumTitle))")
} // each print statement is another way to get the title
2) Print out part of the query to see how it's constructed:
print("xxxx \(qryAlbums.collectionSections)")
I hope this helps some of you - if so up vote!
The idea is simple:
You need to create a sorted array GroupedSongs which holds objects
of type [String: [MPMediaItem]] (key is the character and
the array holds the songs corresponding that character). The
[MPMediaItem] array must be sorted from the title property.
Setting up the Table View Datasource
For each key we need a section which now is the main array
For each item we need a number a songs, so this will be the number
of rows for our tableView
Implementation
Code has comments so no more talking :D
var items: [[String: [MPMediaItem]]] = []
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
items = loadSongsDividedByTitle()
}
// MARK: - Delegation
// MARK: Table view datasource
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let object: [String: [MPMediaItem]] = items[section]
let key = Array(object.keys)[0] // We always have one object there
let songs: [MPMediaItem] = object[key]!
return songs.count
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return items.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:UITableViewCell = UITableViewCell(style: UITableViewCellStyle.Subtitle, reuseIdentifier: "cell")
// Load song from the array
let object: [String: [MPMediaItem]] = items[indexPath.section]
let key = Array(object.keys)[0] // We always have one object there
let songs: [MPMediaItem] = object[key]!
let song = songs[indexPath.row]
// Bind title of the song to the cell text label
cell.textLabel?.text = song.title
return cell
}
// MARK: - Helpers
func loadSongsDividedByTitle() -> [[String: [MPMediaItem]]] {
guard var songs = MPMediaQuery.songsQuery().items else { return [] }
// Sort songs
songs = songs.sort({$0.title < $1.title})
// Create a new dictionary to hold array of words for each letter
var object: [String: [MPMediaItem]] = [:]
// Songs holding a dictionary with music
var groupedSongs : [[String: [MPMediaItem]]] = []
// Enumerate in words
for mediaItem in songs {
guard let title: String = mediaItem.title! else { continue } // If we don't have the title skip the song
// Use the first character as a key for each array
let key = String(title.characters.first!)
if var songGroup = object[key] {
// If we have an array, then append the new word there
songGroup.append(mediaItem)
object[key] = songGroup
} else {
// Create an array for that key
object[key] = [mediaItem]
}
}
// Add every object into one big array
for (key, value) in object {
let sortedSongs = value.sort({$0.title < $1.title})
groupedSongs.append([key: sortedSongs])
}
// Sort it alphabetically from a-z
groupedSongs = groupedSongs.sort({Array($0.keys)[0] < Array($1.keys)[0]})
return groupedSongs
}
Playground output with strings
This has been killing me for a few hours now. I have a UITableViewController that has multiple data sections. My data source is simply an Array.
The problem I'm running into is that each section is repeating data from the array starting from the first index instead of "slicing" it as I expect it should.
Simplified example:
let sections = ["Section A", "Section B"]
let counts = [3, 5]
let source = ["a","b",c","d","e","f","g","h"]
// Output in simulator:
# Section A
- a
- b
- c
# Section B
- a
- b
- c
- d
- e
- and so on...
I would expect that "Section B" would be the next 5 results starting at "d" and not restart from the first index.
The relevant code is pretty standard stuff:
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return sections.count // returns 2
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return counts[section] // returns correct data
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let data = source[indexPath.row]
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! MyTableViewCell
// some cell formatting, populate UILabels, etc
cell.testLabel.text = data["test"].string
return cell
}
override func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerCell = tableView.dequeueReusableCellWithIdentifier("Header") as! MyTableViewHeaderCell
headerCell.backgroundColor = UIColor.grayColor()
headerCell.testHeaderLabel.text = sections[section]
return headerCell
}
Initial searching of SO led me to believe it's a cell reuse issue but after overriding prepareForReuse in my cell class, I don't think thats it.
Expected Results
# Section A
- a
- b
- c
# Section B
- d
- e
- f
- g
- h
Like I said, I'm expecting that dividing the TableView data in to sections would keep a reference to the array pointer and continue where it left off instead of starting back at 0 for each section.
Any thoughts or suggestions?
indexPath.row always returns the row-number inside a section.
In your second section, you need to add the number of rows displayed in all sections before.
Change let data = source[indexPath.row] to something like this:
let data = source[indexPath.row+counts[0]]
If you add more sections, this will be a bit more complicated to calculate.
Other idea:
If it is possible, you could rearrange your array. You could make a two-dimensional array. The main array would include arrays with the data for each section.
To display it, you' need to use indexPath.section, too.
dataArray[indexPath.section][indexPath.row]
Using the idea of FelixSFD, but with a little logical modification, so you can work dynamically:
Change this:
let data = source[indexPath.row]
for this:
var countIndex = indexPath.row
for section in 0...indexPath.section {
countIndex += counts[section]
}
let data = source[countIndex]
Be careful with this approach because you may have some performance issues on large tableViews.
If you can rearrange your array:
change
let source = ["a","b",c","d","e","f","g","h"]
into
let source = [["a","b","c"],["d","e","f","g","h"]]
and change
let data = source[indexPath.row]
into
let data = source[indexPath.section][indexPath.row]
I had the same problem, but with more complex situation, and i needed more dynamically way of doing it. Sure i could rearrange my data, to use two-dimensional array, but i don't want to handle it later. So i did it like this.
I am pulling my data from firebase, so i never know, how many sections/arrays i will have.
Creating an array, to insert amount of items in array.
var counterTableView = [Int]()
Filling array with 0, without doing it, i was getting errors later. (Index out of range)
override func numberOfSections(in tableView: UITableView) -> Int {
for i in 0...Array(Set(self.sections)).count {
counterTableView.insert(0, at: i)
}
counterTableView.removeLast(counterTableView.count-Array(Set(self.sections)).count-1)
return Array(Set(self.sections)).count
}
Next step, is to fill the amount of items in one section in array
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
counterTableView[section+1] = counts[section] + counterTableView[section]}
Last step, showing the data in cell
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
cell.textLabel.text = source[indexPath.row+counterTableView[indexPath.section]]}