Building Subscription Blogging App: Part 8 – View Posts/Feed (2021, Xcode 12, Swift 5) – iOS
UITableViewDiffableDataSource
적용private func applyConstraints() {
postImageView.translatesAutoresizingMaskIntoConstraints = false
postTitleLabel.translatesAutoresizingMaskIntoConstraints = false
let postImageViewConstraints = [
postImageView.leadingAnchor.constraint(equalTo: contentView.safeAreaLayoutGuide.leadingAnchor),
postImageView.widthAnchor.constraint(equalToConstant: contentView.width / 4.0),
postImageView.heightAnchor.constraint(equalToConstant: contentView.width / 4.0),
postImageView.centerYAnchor.constraint(equalTo: contentView.centerYAnchor)
]
NSLayoutConstraint.activate(postImageViewConstraints)
let postTitleLabelConstraints = [
postTitleLabel.topAnchor.constraint(equalTo: contentView.safeAreaLayoutGuide.topAnchor),
postTitleLabel.leadingAnchor.constraint(equalTo: postImageView.trailingAnchor, constant: 5),
postTitleLabel.trailingAnchor.constraint(equalTo: contentView.safeAreaLayoutGuide.trailingAnchor),
postTitleLabel.heightAnchor.constraint(greaterThanOrEqualToConstant: contentView.width / 4.0),
postTitleLabel.bottomAnchor.constraint(equalTo: contentView.safeAreaLayoutGuide.bottomAnchor)
]
NSLayoutConstraint.activate(postTitleLabelConstraints)
}
contentView
크기와 매칭된 오토 레이아웃enum PostDetailSection: Int, CaseIterable, Hashable {
case header
case text
}
enum PostDetailItem: Hashable {
case headerItem(PostModel)
case textItem(PostModel)
}
UITableViewDiffableDataSource
를 관리하기 위한 섹션 및 아이템 이넘화func bind(with tableView: UITableView) {
dataSource = .init(tableView: tableView, cellProvider: { tableView, indexPath, item in
switch item {
case .headerItem(let post):
guard let cell = tableView.dequeueReusableCell(withIdentifier: PostHeaderTableViewCell.identifier, for: indexPath) as? PostHeaderTableViewCell else { return nil }
cell.configure(with: post)
return cell
case .textItem(let post):
guard let cell = tableView.dequeueReusableCell(withIdentifier: PostTextTableViewCell.identifier, for: indexPath) as? PostTextTableViewCell else { return nil }
cell.configure(with: post)
return cell
}
})
applySnapshot()
}
private func applySnapshot() {
var snapshot = NSDiffableDataSourceSnapshot<PostDetailSection, PostDetailItem>()
snapshot.appendSections(PostDetailSection.allCases)
snapshot.appendItems([PostDetailItem.headerItem(post)], toSection: .header)
snapshot.appendItems([PostDetailItem.textItem(post)], toSection: .text)
dataSource.apply(snapshot, animatingDifferences: true)
}
viewDidLoad
단에서 메모리에 올린 테이블 뷰를 파라미터로 건네받아 데이터 소스 이니셜라이즈func configure(with model: PostModel) {
postTitleLabel.text = model.title
if let urlString = model.headerImageURL {
if let image = NSCacheManager.shared.getImage(with: urlString) {
postImageView.image = image
} else {
guard
let urlString = model.headerImageURL,
let url = URL(string: urlString) else { return }
URLSession.shared.dataTask(with: url) { [weak self] data, response, error in
guard
let data = data,
let response = response as? HTTPURLResponse,
response.statusCode >= 200 && response.statusCode < 400,
let image = UIImage(data: data) else { return }
NSCacheManager.shared.setImage(with: urlString, image: image)
DispatchQueue.main.async { [weak self] in
self?.postImageView.image = image
}
}
.resume()
}
}
}
import Foundation
import UIKit
class NSCacheManager {
static let shared = NSCacheManager()
private var imageCache: NSCache<NSString, UIImage> = {
let cache = NSCache<NSString, UIImage>()
cache.countLimit = 100
cache.totalCostLimit = 1024 * 1024 * 1024
return cache
}()
private init() {}
func getImage(with name: String) -> UIImage? {
guard let image = imageCache.object(forKey: name as NSString) else { return nil }
return image
}
func setImage(with name: String, image: UIImage) {
imageCache.setObject(image, forKey: name as NSString)
}
}