I have a Class and I need to get genre object, put in an array, get unique value (because I will have many)
and use the array to populate section of a tableView.
I have the following code:
class Movie {
var name: String!
var plot: String!
var imageUrl: String!
var genre: String!
init(name: String, plot: String, img: String, genre: String) {
self.name = name
self.plot = plot
self.imageUrl = img
self.genre = genre
}
}
}
import UIKit
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
}
var loadMovie = [Movie]()
let insertMovie = Movie(name: "Blow", plot: "Blow bla bla bla", img: "", genre: "Action")
let insertMovie2 = Movie(name: "BLade", plot: "Blade bla bla bla", img: "", genre: "Action")
let insertMovie3 = Movie(name: "Inside Out", plot: "Blow bla bla bla", img: "", genre: "Kids")
let insertMovie4 = Movie(name: "Titanic", plot: "Blow bla bla bla", img: "", genre: "Drama")
loadMovie.append(insertMovie)
loadMovie.append(insertMovie2)
loadMovie.append(insertMovie3)
loadMovie.append(insertMovie4)
let unique = NSSet(array: loadMovie.map { $0.genre })
unique returns the unique value ( Action, Drama, Kids), but when I try to get section:
func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return unique[section].genre
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return unique.count
}
it doesn't work.
Where am I wrong?
Filtering the movies by genre each time the table view is reloaded is quite expensive.
This is a solution using an array of header (genre) names and a dictionary containing a [Movie] array for each genre respectively.
var unique = [String]()
var loadMovie = [String:[Movie]]()
Add a method to insert the movies. The logic is:
• If the genre exists, add the movie for that genre to the [Movie] array in the dictionary.
• If the genre does not exist, add the genre to the unique array and the movie to the dictionary.
func insertMovie(movie : Movie) {
let genre = movie.genre
if unique.contains(genre) {
loadMovie[genre]!.append(movie)
} else {
unique.append(genre)
loadMovie[genre] = [movie]
}
}
Now insert the movies
let movie = Movie(name: "Blow", plot: "Blow bla bla bla", img: "", genre: "Action")
insertMovie(movie)
let movie2 = Movie(name: "BLade", plot: "Blade bla bla bla", img: "", genre: "Action")
insertMovie(movie2)
let movie3 = Movie(name: "Inside Out", plot: "Blow bla bla bla", img: "", genre: "Kids")
insertMovie(movie3)
let movie4 = Movie(name: "Titanic", plot: "Blow bla bla bla", img: "", genre: "Drama")
insertMovie(movie4)
The relevant data source and delegate methods are
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return unique[section]
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return unique.count
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let genre = unique[section]
return loadMovie[genre]!.count
}
In cellForRowAtIndexPath populate the cells with
...
let genre = unique[indexPath.section]
let movie = loadMovie[genre]![indexPath.row]
cell.textLabel!.text = movie.name
...
An alternative is an array of arrays
An update for Swift 4
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return unique[section]
}
func numberOfSections(in tableView: UITableView) -> Int {
return unique.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let genre = unique[section]
return loadMovie[genre]!.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "basicCell", for: indexPath)
let genre = unique[indexPath.section]
let movie = loadMovie[genre]![indexPath.row]
cell.textLabel!.text = movie.name
return cell
}
Related
import UIKit
class MessageDetailViewController: UIViewController {
#IBOutlet weak var tableview: UITableView!
struct data{
let time: String
let message: String
let date: String
let type: String
}
struct section {
let date: String
var array = [data]()
}
let sectionArray = [
section(date: "yesterday", array: [
data(time: "3:30 PM", message: "I am looking for your service, can you please give more information on that.", date: "yesterday", type: "sender"),
data(time: "3:40 PM", message: "Sure i am here to help you", date: "yesterday", type: "receiver")
]),
section(date: "today", array: [data(time: "4:40 PM", message: "Ok, I wil contact you on your phone for that.", date: "today", type: "sender")])
]
override func viewDidLoad() {
super.viewDidLoad()
self.tableview.register(UINib(nibName: "SenderCell", bundle: nil), forCellReuseIdentifier: "SenderCell")
self.tableview.register(UINib(nibName: "ReceiverCell", bundle: nil), forCellReuseIdentifier: "ReceiverCell")
self.tableview.register(UINib(nibName: "customHeaderViewCell", bundle: nil), forCellReuseIdentifier: "customHeaderViewCell")
}
}
extension MessageDetailViewController: UITableViewDelegate, UITableViewDataSource{
func numberOfSections(in tableView: UITableView) -> Int {
return sectionArray.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return sectionArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if sectionArray[indexPath.section].array[indexPath.row].type == "sender"
{
let cell = tableview.dequeueReusableCell(withIdentifier: "SenderCell", for: indexPath) as! SenderCell
cell.setData(reciverTime: sectionArray[indexPath.section].array[indexPath.row].time, reciverMsg: sectionArray[indexPath.section].array[indexPath.row].message)
return cell
}
else
{
let cell = tableView.dequeueReusableCell(withIdentifier: "ReceiverCell", for: indexPath) as! ReceiverCell
cell.setData(reciverTime: sectionArray[indexPath.section].array[indexPath.row].time, reciverMsg: sectionArray[indexPath.section].array[indexPath.row].message)
return cell
}
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 100
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerView = UIView()
let headerCell = tableView.dequeueReusableCell(withIdentifier: "customHeaderViewCell") as! customHeaderViewCell
if sectionArray[section].date == "yesterday"{
headerCell.labelHeader.text = "yesterday"
}
else{
headerCell.labelHeader.text = "today"
}
headerView.addSubview(headerCell)
return headerView
}
}
This is my message detail code.
I want to achieve these
But through these array I achieved these
what changes should I do in my code so that I can get same day message in one section
this ---
sectionArray.append(section(date: "yesterday", array: [data(time: "3:40 PM", message: "SURE i am here to help you", date: "yesterday", type: "receiver")]))
Making a new section I don't want to make new section when sure I am here o help you array comes.
As I've seen your code, you're using String to indicate the day of the message which is bad practice. Instead, you should use Date, which will help you group the messages by date:
let sectionArray = messages.reduce(into: [section]()) { result, element in
if let section = result.firstIndex(where: {Calendar.current.isDate($0.date, inSameDayAs: element.date)}) {
result[section].append(element)//append message to section
}else {
//create new section
}
}
If you really need to use String, use this:
let sectionArray = messages.reduce(into: [section]()) { result, element in
if let section = result.firstIndex(where: {$0.date == element.date}) {
result[section].append(element)//append message to section
}else {
//create new section
}
}
I am trying to use results from a Realm query as section headers in a UITableView.
Realm classes:
class Person: Object {
#objc dynamic var personId = UUID().uuidString
#objc dynamic var firstName: String = ""
#objc dynamic var surname: String = ""
#objc dynamic var mobileNumber: Int = 0
#objc dynamic var password: String = ""
override static func primaryKey() -> String? {
return "personId"
}
}
class Category: Object {
#objc dynamic var categoryId = UUID().uuidString
#objc dynamic var person: Person?
#objc dynamic var categoryName: String = ""
let categoryContent = List<String>()
override static func primaryKey() -> String? {
return "categoryId"
}
}
My code:
class HomeController: UIViewController, UITableViewDelegate, UITableViewDataSource {
let realm = try! Realm()
var itemsInSections: Array<Array<String>> = [["1A"], ["2A"], ["3A"], ["4A"], ["5A"], ["6A"], ["7A"], ["8A"], ["9A"], ["10A"]] //Test content to figure out later
#IBOutlet weak var tableDetails: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableDetails.dataSource = self
tableDetails.delegate = self
}
func numberOfSections(in tableView: UITableView) -> Int {
return getCategoryNames().count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return itemsInSections[section].count
}
private func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> [String] {
return getCategoryNames()
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "HomeTableViewCell", for: indexPath)
let text = self.itemsInSections[indexPath.section][indexPath.row]
cell.textLabel!.text = text
return cell
}
func getCategoryNames() -> [String] {
let categoryNames = realm.objects(Category.self).filter("person.mobileNumber == %#", mobileNumber).map({$0.categoryName})
return Array(categoryNames)
}
}
The number of sections works perfectly, but the section headers are blank.
If I add:
print(Array(categoryNames))
to the getCategoryNames function, it returns ["Category 1", "Category 2", "Category 3", "Category 4"] several times. This seems to be the correct format for the string that is required for the section headers, but the table header is not showing.
Try this:
private func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
let nameArr = getCategoryNames()
return nameArr[section]
}
Function: func tableView(_ tableView: UITableView,
titleForHeaderInSection section: Int) -> String? so you need to return string for a particular section.
My JSON data file is already alphabetized and is properly showing in my TableView. I set up my section titles using:
struct FigureStats: Decodable {
let name: String
let number: String
let year: String?
}
var figSectionTitles = [String]()
var figures = [FigureStats]()
override func viewDidLoad() {
super.viewDidLoad()
figSectionTitles = [String](arrayLiteral: "#", "A", "B", "C")
}
func tableView( tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return figSectionTitles [section]
}
func tableView( tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return figures.count
}
func downloadJSON(completed: #escaping () -> ()) {
let url = URL(string: "https://xxxx")
URLSession.shared.dataTask(with: url!) { (data, response, error) in
if error == nil {
do {
self.figures = try JSONDecoder().decode([FigureStats].self, from: data!)
DispatchQueue.main.async {
completed()
}
} catch {
print("JSON Error")
}
}
}.resume()
}
The problem is all of the entries are repeated inside each section instead of falling under the correct section based on the first character of each name.
How can get the first character of each name and then get those entries to fall under the correct section?
You can use init(grouping:by:) to group the array into Dictionary.
class ViewController: UIViewController,UITableViewDelegate,UITableViewDataSource {
var figures = [Employee]()
var figuresByLetter = [(key: String, value: [Employee])]()
override func viewDidLoad() {
super.viewDidLoad()
figures = [Employee(name: "Kofi", year: 2000, id: 1),Employee(name: "Abena", year: 2002, id: 2),Employee(name: "Efua", year: 2003, id: 3),Employee(name: "Kweku", year: 2003, id: 3),Employee(name: "Akosua", year: 2003, id: 3)]
figuresByLetter = Dictionary(grouping: figures, by: { String($0.name.trimmingCharacters(in: .whitespaces).first!) }).sorted(by: { $0.0 < $1.0 })
print(figuresByLetter)
}
func numberOfSections(in tableView: UITableView) -> Int {
return figuresByLetter.count
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return figuresByLetter[section].key
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return figuresByLetter[section].value.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") ?? UITableViewCell(style: .default, reuseIdentifier: "cell")
cell.textLabel?.text = figuresByLetter[indexPath.section].value[indexPath.row].name
return cell
}
}
struct Employee {
var name:String?
var year:Int?
var id:Int?
}
Data after grouping
[(key: "A", value: [Employee(name: "Abena", year: 2002, id: 2), Employee(name: "Akosua", year: 2003, id: 3)]),
(key: "E", value: [Employee(name: "Efua", year: 2003, id: 3)]),
(key: "K", value: [Employee(name: "Kofi", year: 2000, id: 1), Employee(name: "Kweku", year: 2003, id: 3)])]
func numberOfSections(in tableView: UITableView) -> Int {
return figuresByLetter.count
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
let sectionTitle = figuresByLetter[indexPath.section]
return sectionTitle
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let sectionTitle = figuresByLetter[indexPath.section]
//filteredArray = filter the array of figures based on current section value
return filteredArray.count
}
I am working on a table view project I've seen in a tutorial, then I came across this piece of code that gives me the **error: Definition conflicts with previous value.**
The piece of code is:
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int, titleForHeaderInSection section:Int) -> String? {
// Return the number of rows in the section.
return animalSelectionTitles[section]
}
I have tried to change the String? into String or Int, but String gives me the same error and Int gives me an error on the return line.
Here's my complete code:
import UIKit
class AnimalTableViewController: UITableViewController {
var animalsDict = [String: [String]] ()
var animalSelectionTitles = [String] ()
let animals = ["Bear", "Black Swan", "Buffalo", "Camel", "Cockatoo", "Dog", "Donkey", "Emu", "Giraffe", "Greater Rhea", "Hippopotamus", "Horse", "Koala", "Lion", "Llama", "Manatus", "Meerkat", "Panda", "Peacock", "Pig", "Platypus", "Polar Bear", "Rhinoceros", "Seagull", "Tasmania Devil", "Whale", "Whale Shark", "Wombat"]
func createAnimalDict() {
for animal in animals {
let animalKey = animal.substringFromIndex(advance(animal.startIndex, 1))
if var animalValues = animalsDict[animalKey] {
animalValues.append(animal)
animalsDict[animalKey] = animalValues
} else {
animalsDict[animalKey] = [animal]
}
}
animalSelectionTitles = [String] (animalsDict.keys)
animalSelectionTitles.sort({ $0 < $1})
animalSelectionTitles.sort( { (s1:String, s2:String) -> Bool in
return s1 < s2
})
}
override func viewDidLoad() {
super.viewDidLoad()
createAnimalDict()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// Return the number of sections.
return animalSelectionTitles.count
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int, titleForHeaderInSection section:Int) -> String? {
// Return the number of rows in the section.
return animalSelectionTitles[section]
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! UITableViewCell
// Configure the cell...
cell.textLabel?.text = animals[indexPath.row]
// Convert the animal name to lower case and
// then replace all occurences of a space with an underscore
let imageFilename = animals[indexPath.row].lowercaseString.stringByReplacingOccurrencesOfString(" ", withString: "_", options: nil, range: nil)
cell.imageView?.image = UIImage(named: imageFilename)
return cell
}
Two things:
1)
You should change your line
let animalKey = animal.substringFromIndex(advance(animal.startIndex, 1))
Currently it substrings from the 2nd character, which means that for the input Black Swan then animalKey would be equal to lack Swan. Instead you should use the following line:
let animalKey = animal.substringToIndex(advance(animal.startIndex, 1))
2)
There is no method in the UITableViewDataSource Protocol which is called tableView:numberOfRowsInSection:titleForHeaderInSection. Instead you need to split it into the following two methods:
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let title = animalSelectionTitles[section]
return animalsDict[title]!.count
}
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return animalSelectionTitles[section]
}
UPDATE 1:
In your tableView:cellForRowAtIndexPath, you should also update the retrieving of the animal name to reflect what is stored in the dictionary like so:
// Configure the cell...
let secTitle = animalSelectionTitles[indexPath.section]
let animalName = animalsDict[secTitle]![indexPath.row]
cell.textLabel?.text = animalName
I'm trying to pass array values to a tableview.
# //4 println(items) it prints an array with values to my console. I want to have those results passed in to the tableview. The tableview is working but gets it's value from var list: [String] = ...
How do I get the println(items) in the var list: [String] = ... ?
Thanks!!
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
var list: [String] = ["Row One", "Row Two", "Row Three" , "Row Four", "Row Five"]
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "cell")
var results = "today"
var subItem = "movie"
// hier moet de opegslagen bioscoop komen die door de gebruiker is opgeslagen als keuze
var cinemaFilter = "Pathé Arena"
// Get the Cinema's from scraper and SwiftyJSON
DataManager.getCinemaDataFromScraperWithSuccess { (ScraperData) -> Void in
let json = JSON(data: ScraperData)
if let itemName = json["results"][results][0][subItem]["text"].stringValue {
println("Film resultaten:")
}
//1
if let itemArray = json["results"][results].arrayValue {
//2
var items = [itemModel]()
//3
for itemDict in itemArray {
var itemName: String? = itemDict[subItem]["text"].stringValue
var itemTime: String? = itemDict["time"]["text"].stringValue
var itemCinema: String? = itemDict["cinema"]["text"].stringValue
if itemCinema == cinemaFilter {
var item = itemModel(name: itemName, time: itemTime, cinema: itemCinema )
items.append(item)
} else {
//println("is niet de ingestelde bioscoop")
}
}
//4
println(items)
}
}
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.list.count;
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell:UITableViewCell = self.tableView.dequeueReusableCellWithIdentifier("cell") as UITableViewCell
cell.textLabel?.text = self.list[indexPath.row]
return cell
}
func tableView(tableView: UITableView!, didSelectRowAtIndexPath indexPath: NSIndexPath!) {
println("You selected cell #\(indexPath.row)!")
}
}
Make items an instance variable of your view controller instead of a local variable, use it in the data source methods, and get rid of list.
You then need to decide what part of an item you want to show in cell.textLabel?.text, name, time, cinema, or some combination.
Instead of var items = [itemModel]() inside the func place it on class lavel so it's an instance. Then change
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.items.count;
}
and the cell method accordingly to use items.