I'm new to this, so this question might be stupid.
I have made some indexed table view with user names. Now I added a label with user surnames and I want to make my indexed table view sorted by user's surnames using data model and I just really have no idea how to do that.
It may be helpful, so here you can watch how my app is working right now.
The first, there are FriendsSearchViewController
with all users template.
import UIKit
final class FriendsSearchViewController: UITableViewController {
var friends = [
"Polina",
"Ivan",
"Pavel",
"Maria",
"Nick"
]
var userFriends: [String] = []
var friendSectionTitles = [String]()
var friendsDictionary = [String: [String]]()
// MARK: - Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(UINib(
nibName: "FriendCell",
bundle: nil),
forCellReuseIdentifier: "friendCell")
for friend in friends {
let friendKey = String(friend.prefix(1))
if var friendValues = friendsDictionary[friendKey] {
friendValues.append(friend)
friendsDictionary[friendKey] = friendValues
} else {
friendsDictionary[friendKey] = [friend]
}
}
friendSectionTitles = [String](friendsDictionary.keys)
friendSectionTitles = friendSectionTitles.sorted(by: { $0 < $1 })
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
friendSectionTitles.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let friendKey = friendSectionTitles[section]
if let friendValues = friendsDictionary[friendKey] {
return friendValues.count
}
return 0
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
friendSectionTitles[section]
}
override func sectionIndexTitles(for tableView: UITableView) -> [String]? {
friendSectionTitles
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard
let cell = tableView.dequeueReusableCell(withIdentifier: "friendCell", for: indexPath) as? FriendCell
else { return UITableViewCell() }
var currentFriend = friends[indexPath.row]
let friendKey = friendSectionTitles[indexPath.section]
if let friendValues = friendsDictionary[friendKey] {
currentFriend = friendValues[indexPath.row]
}
cell.configure(
photo: UIImage(named: "\(indexPath.row)") ?? UIImage(),
name: currentFriend,
surname: "")
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
defer {
tableView.deselectRow(at: indexPath, animated: true)
}
let friendKey = friendSectionTitles[indexPath.section]
var currentFriend = ""
if let friendValues = friendsDictionary[friendKey] {
currentFriend = friendValues[indexPath.row]
}
if userFriends.firstIndex(of: currentFriend) == nil {
userFriends.append(currentFriend)
}
self.performSegue(withIdentifier: "addFriend", sender: nil)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "addFriend",
let myFriendsViewController = segue.destination as? MyFriendsViewController {
myFriendsViewController.friends = userFriends
}
}
}
And there are MyFriendsViewController
with users that have been added to friends list:
import UIKit
final class MyFriendsViewController: UITableViewController {
var friends = [String]() {
didSet {
//
}
}
@IBAction func addFriend(segue: UIStoryboardSegue) {
guard segue.identifier == "addFriend",
let allFriendsViewController = segue.source as? FriendsSearchViewController
else { return }
friends = allFriendsViewController.userFriends
tableView.reloadData()
}
// MARK: - Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(UINib(
nibName: "FriendCell",
bundle: nil),
forCellReuseIdentifier: "friendCell")
}
// MARK: - Table view data source
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
friends.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard
let cell = tableView.dequeueReusableCell(withIdentifier: "friendCell", for: indexPath) as? FriendCell
else { return UITableViewCell() }
let currentFriend = friends[indexPath.row]
cell.configure(
photo: UIImage(named: "\(indexPath.row)") ?? UIImage(),
name: currentFriend,
surname: "")
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
defer { tableView.deselectRow(
at: indexPath,
animated: true)}
performSegue(
withIdentifier: "showProfile",
sender: nil)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "addFriend",
let allFriendsViewController = segue.destination as? FriendsSearchViewController {
allFriendsViewController.userFriends = friends
}
}
}
Also there are UserModel
that probably looks not correctly:
import UIKit
struct UserModel {
let userName: String
let userSurname: String
let userPhoto: UIImage
let userAge: String
}
And FriendCell
with cell configuration:
import UIKit
class FriendCell: UITableViewCell {
@IBOutlet var friendPhoto: AvatarImage!
@IBOutlet var friendName: UILabel!
@IBOutlet var friendSurname: UILabel!
func configure(
photo: UIImage,
name: String,
surname: String) {
self.friendPhoto.image = photo
self.friendName.text = name
self.friendSurname.text = surname
}
}
I'm just cannot imagine what should I do. How do I should made it? Please, can you give me some ideas with code examples? Thank you!
CodePudding user response:
1.
Make some small adjustments to the UserModel
// Make UserModel conform to Equatable protocol so you can
// compare two UserModel Objects using == or firstIndexOf
// Read more on Equatable here: https://developer.apple.com/documentation/swift/equatable
struct UserModel: Equatable {
// I changed this to userFirstName to be more clear
let userFirstName: String
let userSurname: String
// I made UIImage? optional, but you can change if you wish
let userPhoto: UIImage?
let userAge: Int
}
2.
Add struct to array instead of strings in FriendsSearchViewController
// Update friends array to hold structs of people
var friends = [
UserModel(userFirstName: "Polina",
userSurname: "James",
userPhoto: UIImage(systemName: "star"),
userAge: 20),
UserModel(userFirstName: "Ivan",
userSurname: "Gomez",
userPhoto: UIImage(systemName: "star"),
userAge: 20),
UserModel(userFirstName: "Pavel",
userSurname: "Harvey",
userPhoto: UIImage(systemName: "star"),
userAge: 20),
UserModel(userFirstName: "Maria",
userSurname: "Fernando",
userPhoto: UIImage(systemName: "star"),
userAge: 20),
UserModel(userFirstName: "Nick",
userSurname: "Cage",
userPhoto: UIImage(systemName: "star"),
userAge: 20),
UserModel(userFirstName: "Shawn",
userSurname: "Frank",
userPhoto: UIImage(systemName: "star"),
userAge: 20)
]
3.
Change these to be struct arrays, not String in FriendsSearchViewController
// userFriends should now be an array of UserModel type, not String
var userFriends: [UserModel] = []
var friendSectionTitles = [String]()
// Also friendsDictionary should be an array of UserModel type, not String
var friendsDictionary = [String: [UserModel]]()
4.
Adjust loop in viewDidLoad
to work with Structs as they will not be strings anymore in FriendsSearchViewController
// Now friend will be of type UserModel, not string
for friend in friends {
// So we need to check the prefix of the UserMode.userSurname
let friendKey = String(friend.userSurname.prefix(1))
if var friendValues = friendsDictionary[friendKey] {
friendValues.append(friend)
friendsDictionary[friendKey] = friendValues
} else {
friendsDictionary[friendKey] = [friend]
}
}
friendSectionTitles = [String](friendsDictionary.keys)
friendSectionTitles = friendSectionTitles.sorted(by: { $0 < $1 })
5. Adjust tableView datasource functions to work with structs instead of strings
Fix this line cellForRowAt
in both ViewControllers
// Now you need to get the UserModel.userFirstName and UserModel.userSurname
cell.textLabel?.text = "\(currentFriend.userFirstName) \(currentFriend.userSurname)"
Fix this line in didSelectRowAt
in FriendsSearchViewController
// This should not be a string
var currentFriend = friends[indexPath.row]
6.
Change friends array from string to UserModel in MyFriendsViewController
var friends = [UserModel]() {
didSet {
//
}
}
7. Now to sort friends list by surname, adjust your exit segue function to sort the friends array
@IBAction func addFriend(segue: UIStoryboardSegue) {
guard segue.identifier == "addFriend",
let allFriendsViewController = segue.source as? FriendsSearchViewController
else { return }
// Sort data when you come back
friends = allFriendsViewController.userFriends.sorted {
$0.userSurname < $1.userSurname
}
tableView.reloadData()
}
That should give you desired result
However, please read on these topics for you to fully grasp these concepts:
Final recommendation is to break your problem into smaller parts for yourself and also for stack overflow.
For example here there were few different topics like how to create model, how to use struct with arrays, how to sort array.
Having one topic per question will help you get answer sooner but it is good you are trying things and asking questions.