생성일: 2022년 1월 6일 오후 11:47
인스타 그램에서의 탭바 첫 항목인 Feed 화면을 구성하기 위해 CollectionView를 사용한다.
import UIKit
private let reuseIdentifier = "Cell"
class FeedController: UICollectionViewController {
//MARK: - Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
configureUI()
}
//MARK: - Helpers
func configureUI() {
collectionView.backgroundColor = .white
// 콜렉션 뷰에 cell 등록
collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: reuseIdentifier)
}
}
//MARK: - UICollectionViewDataSource
extension FeedController {
// collectionView에 들어갈 아이템의 개수 정의
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 5
}
// collectionView의 각 cell을 정의
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath)
cell.backgroundColor = .red
return cell
}
}
//MARK: - UICollectionViewDelegateFlowLayout
// 각 cell의 크기 정의
extension FeedController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: view.frame.width, height: 200)
}
}
여기까지 설정하고 실행시키면 오류가 발생한다.
Why?
이를 해결하기 위해
func configureViewControllers() {
view.backgroundColor = .white
let layout = UICollectionViewFlowLayout()
let feed = templateNavigationController(unselectedImage: #imageLiteral(resourceName: "home_unselected"), selectedImage: #imageLiteral(resourceName: "home_selected"), rootViewController: FeedController(collectionViewLayout: layout))
...생략
}
위와 같이 layout을 생성하고 객체 선언시에 FeedController 클래스에 넘겨주었다.
View 폴더에 FeedCell.swift를 생성한다.
스토리보드를 사용한다면 xib파일로 cell을 그릴수도 있지만 코드로 ui를 만드는 방법에서는 swift 파일에서 ui를 코드로 만들어야 한다.
import UIKit
class FeedCell: UICollectionViewCell {
//MARK: - Properties
private let profileImageView: UIImageView = {
let iv = UIImageView()
iv.contentMode = .scaleAspectFill
iv.clipsToBounds = true
iv.isUserInteractionEnabled = true
iv.image = #imageLiteral(resourceName: "venom-7")
return iv
}()
// 여기서 lazy로 선언하는 이유는 addTarget 때문이다. lazy로 하지 않으면 addTarget은 usernameButton 객체가 생성되기 전에 작동하는데 당연히 객체가 없는데 Target을 설정하는 것은 문제를 발생시킨다. 따라서 lazy를 통해 확실히 usernameButton이 생성된 후 addTarget이 작동하도록 해야한다.
private lazy var usernameButton: UIButton = {
let button = UIButton(type: .system)
button.setTitleColor(.black, for: .normal)
button.setTitle("venom", for: .normal)
button.titleLabel?.font = UIFont.boldSystemFont(ofSize: 13)
button.addTarget(self, action: #selector(didTapUsername), for: .touchUpInside)
return button
}()
private let postImageView: UIImageView = {
let iv = UIImageView()
iv.contentMode = .scaleAspectFill
iv.clipsToBounds = true
iv.isUserInteractionEnabled = true
iv.image = #imageLiteral(resourceName: "venom-7")
return iv
}()
private lazy var likeButton: UIButton = {
let button = UIButton(type: .system)
button.setImage(#imageLiteral(resourceName: "like_unselected"), for: .normal)
button.tintColor = .black
return button
}()
private lazy var commentButton: UIButton = {
let button = UIButton(type: .system)
button.setImage(#imageLiteral(resourceName: "comment"), for: .normal)
button.tintColor = .black
return button
}()
private lazy var shareButton: UIButton = {
let button = UIButton(type: .system)
button.setImage(#imageLiteral(resourceName: "send2"), for: .normal)
button.tintColor = .black
return button
}()
private let likesLabel: UILabel = {
let label = UILabel()
label.text = "1 like"
label.font = UIFont.boldSystemFont(ofSize: 13)
return label
}()
private let captionLabel: UILabel = {
let label = UILabel()
label.text = "Some test caption"
label.font = UIFont.boldSystemFont(ofSize: 14)
return label
}()
private let postTimeLabel: UILabel = {
let label = UILabel()
label.text = "2 days ago"
label.font = UIFont.systemFont(ofSize: 12)
label.textColor = .lightGray
return label
}()
//MARK: - Lifecycle
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .white
addSubview(profileImageView)
profileImageView.anchor(top: topAnchor, left: leftAnchor, paddingTop: 12, paddingLeft: 12)
profileImageView.setDimensions(height: 40, width: 40)
profileImageView.layer.cornerRadius = 40 / 2
addSubview(usernameButton)
usernameButton.centerY(inView: profileImageView, leftAnchor: profileImageView.rightAnchor, paddingLeft: 8)
addSubview(postImageView)
postImageView.anchor(top: profileImageView.bottomAnchor, left: leftAnchor, right: rightAnchor, paddingTop: 8)
postImageView.heightAnchor.constraint(equalTo: widthAnchor, multiplier: 1).isActive = true
configureActionButtons()
addSubview(likesLabel)
likesLabel.anchor(top: likeButton.bottomAnchor, left: leftAnchor, paddingTop: -4, paddingLeft: 8)
addSubview(captionLabel)
captionLabel.anchor(top: likesLabel.bottomAnchor, left: leftAnchor, paddingTop: 8, paddingLeft: 8)
addSubview(postTimeLabel)
postTimeLabel.anchor(top: captionLabel.bottomAnchor, left: leftAnchor, paddingTop: 8, paddingLeft: 8)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
//MARK: - Actions
@objc func didTapUsername() {
print("DEBUG: did tap username")
}
//MARK: - Helpers
func configureActionButtons() {
let stackView = UIStackView(arrangedSubviews: [likeButton, commentButton, shareButton])
stackView.axis = .horizontal
stackView.distribution = .fillEqually
addSubview(stackView)
stackView.anchor(top: postImageView.bottomAnchor, width: 120, height: 50)
}
}
코드로 ui를 만드는 것은 기본적으로 다음과 같은 과정을 거친다.
ui객체 생성하기
private let profileImageView: UIImageView = {
let iv = UIImageView()
iv.contentMode = .scaleAspectFill
iv.clipsToBounds = true
iv.isUserInteractionEnabled = true
iv.image = #imageLiteral(resourceName: "venom-7")
return iv
}()
init 함수에 추가하기
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .white
addSubview(profileImageView)
profileImageView.anchor(top: topAnchor, left: leftAnchor, paddingTop: 12, paddingLeft: 12)
profileImageView.setDimensions(height: 40, width: 40)
profileImageView.layer.cornerRadius = 40 / 2
...
}