미니 프로젝트로 팀원분들과 subway 주문 화면을 만들어 보기로 했다!

구상한 화면은 다음과 같다. 여기서 top, body, bottom으로 3부분으로 나누어 작업하기로 했고 나는 top 부분을 맡았다. 스토리보드로 구현하기로 했으면 협업은 깃을 활용했다. 사실 하나의 뷰컨트롤러를 여러명이 동시에 작업한다는 점이 충돌을 피하기 어렵겠지만 일단 도전해보기로 했다.
먼저 topView 에서 titleView와 menuView로 구분하고 만들었다.
titleView는 UIView위에 UILabel을 올리기만 하면 된다.
콜렉션뷰를 사용해서 상단 탭바를 만들어보았다. 콜렉션뷰는 테이블 뷰와 마찬가지로 dataSource와 delegate를 지정해주어야 했고 셀을 등록시켜줘야 했다. 콜렉션 뷰를 만드는 것은 어렵지 않았으나 다음과 같은 사항을 지키는 것이 중요했다.
먼저 가로스크롤을 구현하기 위해 collectionViewFlowLayout()이라는 클래스를 사용하였다.
이 클래스는 컬렉션 뷰의 레이아웃을 정의하고 사용자가 정의한 규칙에 따라 셀을 배치한다. 여기서 말하는 레이아웃은 화면에 표시되는 요소들의 위치, 크기, 간격 등을 말한다고 생각하면 된다.

공식 문서에 나와있는 내용을 보면 이 클래스의 scrollDirection 프로퍼티를 활용하여 스크롤 방향을 수직이나 수평으로 바꿔줄 수 있다.
func setTabbarTop(){
let flowlayout = UICollectionViewFlowLayout()
flowlayout.scrollDirection = .horizontal //가로스크롤!!
flowlayout.minimumLineSpacing = 15
flowlayout.sectionInset = UIEdgeInsets(top:0, left:0, bottom: 0, right: 0)
}
뿐만 아니라 .minimumLineSpacing 으로 아이템간의 간격을 정할 수 있었고, .sectionInset 으로 셀과 섹션의 가장자리 간의 간격을 조절할 수 있다.
collectionView는 collectionFlowLayout을 다음과 같이 지정해주었다ㅏ.
let topCollectionViewFlowLayout: UICollectionViewFlowLayout = {
let flowlayout = UICollectionViewFlowLayout()
flowlayout.scrollDirection = .horizontal
flowlayout.minimumLineSpacing = 15
flowlayout.sectionInset = UIEdgeInsets(top:0, left:0, bottom: 0, right: 0)
return flowlayout
}()
scrollDirection은 .horizontal로 가로로 스크롤되도록 설정하였고, .minumumLineSpacing으로 아이템의 세로간격을 15포인트로 설정하였다. 그리고 .sectionInset으로 모든 여백을 0으로 설정하여 섹션 내부의 여백을 제거하였다.
그리고 해당 collectionView가 topCollectionViewFlowLayout을 따를 수 있도록 설정해주었다. 여기서 frame: .zero는 초기 프레임이 없고 추후에 크기를 설정해주어야 한다는 뜻이다. (나는 오토레이아웃으로 추후에 크기와 위치를 설정하였다.)
lazy var tabbarTop: UICollectionView = {
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: topCollectionViewFlowLayout)
collectionView.translatesAutoresizingMaskIntoConstraints = false
return collectionView
}()
콜렉션뷰의 셀이 메뉴의 글자 길이에 맞는 크기를 가질 수 있도록 하기 위해서는 텍스트의 길이를 알아야한다. 이때 맨처음에는 깊은 생각없이 text.count를 했더니 텍스트의 문자길이가 나왔다.🥲 바보같은 실수였다..ㅎ 그래서 textWidth를 구하기 위해 찾아보니 text의 사이즈를 구할 수 있는 코드를 찾았다.
let text = menuList[indexPath.row]
let textWidth = text.size(withAttributes: [NSAttributedString.Key.font: font]).width
text의 size(withAttributes:)메서드는 NSAttributedString의 확장 메서드로, 문자열이 주어진 속성과 폰트로 렌더링될 때의 크기를 반환한다. 속성으로 전달되는 딕셔너리인 [NSAttributedString.Key.font: font]는 문자열의 폰트를 설정하는 키와 문자열에 적용할 폰트로 구성되어 있다.
그렇게 셀을 textWidth보다 20정도 크게 하는 코드는 다음과 같다.
let text = menuList[indexPath.row]
let font = UIFont.systemFont(ofSize: 20)
let textWidth = text.size(withAttributes: [NSAttributedString.Key.font: font]).width
let cellWidth = textWidth + 20
return CGSize(width: cellWidth, height: 60)
클릭 시에 글자의 색이 바뀌거나 배경 색이 바뀌도록 해야했다. didSelectItemAt 메서드를 사용해서 구현하려고 했지만 검색결과 cell 클래스에서 해결할 수 있는 방법이 있었다.
isSelected 속성을 오버라이드하여 해당 뷰가 선택되었을 때와 선택되지 않았을 대의 UI를 변경할 수 있다. 해당 뷰의 isSelected에 프로퍼티 감시자를 정의하여 다음과 같이 구현하였다.
override var isSelected: Bool {
didSet{
if isSelected {
backView.backgroundColor = UIColor.lightGray
menuText.textColor = .black
}
else {
menuText.textColor = .gray
backView.backgroundColor = UIColor.white
}
}
}
그래서 만약 셀을 하나 선택하면 그 셀의 배경은 lightGray로 텍스트 색은 Black으로 변경된다. 이는 여러개의 셀을 선택할때는 구현하지 못하고 하나의 셀만 선택하는 경우에만 가능한 방법일 것이다.
스토리보드를 통해 구현한 TopView는 다음과 같다.
깃허브링크
