Displaying a Collection of Users
User 구조체 정의
struct User {
let id: String
let name: String
let color: Color?
let bio: String?
}
extension User: Codable {}
extension User: Hashable {
func hash(into hasher: inout Hasher) {
hasher.combine(id)
}
static func == (lhs: User, rhs: User) -> Bool {
lhs.id == rhs.id
}
}
Settings.swift 파일 Setting 열거형에 "followedUserIDs" key 추가 및 followed users IDs를 불러오고 저장하기 위한 프로퍼티 선언
enum Setting {
static let favoriteHabits = "favoriteHabits"
static let followedUserIDs = "followedUserIDs"
}
var followedUserIDs: [String] {
get {
return unarchiveJSON(key: Setting.followedUserIDs) ?? []
}
set {
archiveJSON(value: newValue, key: Setting.followedUserIDs)
}
}
UserCollectionViewController에 User 관련 기본 정의
typealias DataSourceType = UICollectionViewDiffableDataSource<ViewModel.Section, ViewModel.Item>
enum ViewModel {
typealias Section = Int
struct Item: Hashable {
let user: User
let isFollowed: Bool
func hash(into hasher: inout Hasher) {
hasher.combine(user)
}
static func ==(_ lhs: Item, _ rhs: Item) -> Bool {
return lhs.user == rhs.user
}
}
}
struct Model {
var userByID = [String:User]()
var followedUsers: [User] {
return Array(userByID.filter{ Settings.shared.followedUserIDs.contains($0.key) }.values)
}
}
var dataSource: DataSourceType!
var model = Model()
Add Networking and Snapshot Code
APIService에 새로운 API Request 타입 정의
struct UserRequest: APIRequest {
typealias Response = [String: User]
var path: String { "/users" }
}
User를 이름에 따라 분류하기 위해 User 구조체 Comparable 프로토콜 채택
extension User: Comparable {
static func < (lhs: User, rhs: User) -> Bool {
return lhs.name < rhs.name
}
}
Users 불러오는 메서드 정의
func update() {
usersRequestTask?.cancel()
usersRequestTask = Task {
if let users = try? await UserRequest().send() {
self.model.userByID = users
} else {
self.model.userByID = [:]
}
self.updateCollectionView()
usersRequestTask = nil
}
}
func updateCollectionView() {
let users = model.userByID.values.sorted().reduce(into: [ViewModel.Item]()) { partial, user in
partial.append(ViewModel.Item(user: user, isFollowed: model.followedUsers.contains(user)))
}
let itemBySection = [0: users]
dataSource.applySnapshotUsing(sectionIDs: [0], itemsBySection: itemBySection)
}
Set Up the Layout and Data Source
HabitCollectionViewController와 비슷하다.
createDataSource() 메서드
func createDataSource() -> DataSourceType {
let dataSource = DataSourceType(collectionView: collectionView) { (collectionView, indexPath, item) in
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "User", for: indexPath) as! UICollectionViewListCell
var content = cell.defaultContentConfiguration()
content.text = item.user.name
content.directionalLayoutMargins = NSDirectionalEdgeInsets(top: 11, leading: 8, bottom: 11, trailing: 8)
content.textProperties.alignment = .center
cell.contentConfiguration = content
return cell
}
return dataSource
}
createLayout() 메서드
func createLayout() -> UICollectionViewCompositionalLayout {
let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .fractionalHeight(1))
let item = NSCollectionLayoutItem(layoutSize: itemSize)
let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .fractionalHeight(0.45))
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, repeatingSubitem: item, count: 2)
group.interItemSpacing = .fixed(20)
let section = NSCollectionLayoutSection(group: group)
section.interGroupSpacing = 20
section.contentInsets = NSDirectionalEdgeInsets(top: 20, leading: 20, bottom: 20, trailing: 20)
return UICollectionViewCompositionalLayout(section: section)
}
viewDidLoad()
override func viewDidLoad() {
super.viewDidLoad()
dataSource = createDataSource()
collectionView.dataSource = dataSource
collectionView.collectionViewLayout = createLayout()
update()
}
Implement the User Following Context Menu
Settings
mutating func toggleFollowed(user: User) {
var updated = followedUserIDs
if updated.contains(user.id) {
updated = updated.filter { $0 != user.id }
} else {
updated.append(user.id)
}
followedUserIDs = updated
}
UserCollectionViewController
override func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemsAt indexPaths: [IndexPath], point: CGPoint) -> UIContextMenuConfiguration? {
let config = UIContextMenuConfiguration(identifier: nil, previewProvider: nil) { (elements) -> UIMenu? in
guard let item = self.dataSource.itemIdentifier(for: indexPaths.first!) else { return nil }
let favoriteToggle = UIAction(title: item.isFollowed ? "Unfollowed" : "Follow") { (action) in
Settings.shared.toggleFollowed(user: item.user)
self.updateCollectionView()
}
return UIMenu(title: "", image: nil, identifier: nil, options: [], children: [favoriteToggle])
}
return config
}
'Develop in Swift Data Collections' 카테고리의 다른 글
Guided Project: Habits - 5 (0) | 2024.06.18 |
---|---|
Guided Project: Habits - 4 (0) | 2024.06.17 |
Guided Project: Habits - 2 (0) | 2024.06.13 |
Guided Project: Habits - 1 (0) | 2024.06.13 |
Lesson 3.5 Local Notifications - Practice(Alarm) (0) | 2024.06.06 |