// 버튼 이름이 refreshButton 이라 하면
refreshButton.layer.cornerRadius = 10
'layer' 는 렌더링에 사용되는 뷰의 코어 애니메이션 레이어라고 한다. 일반적인 UIView
와는 다르고, 코어 애니메이션에서 제공하는 클래스이다. UIView
와 달리 별도의 Responder
가 없어 유저 인터렉션 기능은 직접 구현 및 설정 해야한다.
UIView
와는 달리 메인스레드를 사용하지 않고, 별도의 스레드를 사용한다.
UIView
는 사실 CALayer
의 wrapper
이다. UIView
는 많은 것을 처리하지만 사실 어떤 그림이나 애니메이션들은 직접 제어하지 않고 내가 view
에 어떤 일을 시키면 이 안의 layer
객체에서 이 일을 직접 수행한다.
예를 들어, view
의 backgroundColor
설정을 해도 결국엔, layer
의 backgroundColor
가 변하는 것이다.
// 컬렉션 뷰의 수직 바운드를 막아줌. (수평방향일때 위아래 드래그 불가능하게 해줌)
collectionView.alwaysBounceVertical = false
// 만약 섹션의 크기가 부족하면 시스템이 자동으로 다음 셀을 밑으로 보내버리는데 그 부분을 방지해줌
section.orthogonalScrollingBehavior = .continuous
// 페이징을 쉽게 해주는 프로퍼티, 그룹을 가운데로 정렬해서 페이징 효과를 준다.
section.orthogonalScrollingBehavior = .groupPagingCentered
// 현재 보여지고 있는 화면에 어떤 항목이 표시되는지 알 수 있는 api
// 내부 클로저를 활용해서 처리한다.
section.visibleItemsInvalidationHandler = { (item, offset, env) in
// 소수점 올림하는 코드, 페이징에 값을 넘기기 위해 계산 !
let index = Int((offset.x / env.container.contentSize.width).rounded(.up))
self.pageControl.currentPage = index
}
모달은 사용자에게 일시적으로 집중을 요하는 컨텐츠 표시할 때 사용
따라서 사용자에게 모달로 띄운 화면이 중요하고, 필요한 경우 액션이 요구 됨을 알려줌
화면 나가기 위한 명확한 행동이 요구됨(닫기버튼, 화면 스와이프 다운 등)
보통 사용자들은 앱의 기능을 탐색할때, 타고 타고 들어가다가 빠져 나올 때 당황하거나 어려움을 겪음
이때 네비게이션은 사용자가 당황하지 않게끔, 자연스럽게 지나왔던 곳을 돌아가게끔 도와주는 것
iOS 에서 제공해주는 시스템 모달
개발자가 직접 제공해주는 모달
직접 제공해주는 경우, 모달 띄우는 스타일도 정해야함
구현
뷰 컨트롤러의 present(_:animated:completion:)
메소드를 사용
iOS 에서 주요 네비게이션 스타일 3가지
여러 스타일을 나누긴 했지만, 앱 내에서는 각 스타일을 복합적으로 사용함
요약
사용자가 앱내에서 화면전환간에 헤매지 않도록 도와주어야함 크게는 네비게이션, 모달을 적절히 사용해서 도움 모달은 집중을, 네비게이션은 개미지옥 탈출 을 도와줌
스택 뷰안의 이미지를 넣을때 뷰 하나를 만든 다음 넣어라.
스택 뷰에서 이미지는 자동으로 스택 뷰 너비만큼 맞춰서 늘어나기 떄문임
// import 하고 아래 코드를 작성하면 된다 (버튼 안에 넣어주거나 등등)
import SafariServices
guard let url = URL(string: framework.urlString) else {
return
}
let safari = SFSafariViewController(url: url)
present(safari, animated: true)
// 먼저 delegate 설정 후에 밑의 코드를 작성 하면 된다.
// 이 경우에는 컬렉션 뷰의 아이템을 선택하면 아래 코드를 수행하겠다는 의미이다.
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let framework = list[indexPath.item]
print(">>> selected: \(framework.name)")
// 스토리보드의 이름과 뷰 컨트롤러의 ID를 들고 오는 부분
let storyboard = UIStoryboard(name: "Detail", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "FrameworkDetailViewController") as! FrameworkDetailViewController
// 데이터를 넘겨주는 부분
vc.framework = framework
// 만약 풀스크린으로 띄우고 싶다면 아래 코드 작성
// vc.UIModalPresentationStyle = .fullScreen
// 화면을 띄우는 부분
present(vc, animated: true)
}
만약 같은 스토리보드의 다른 뷰 컨트롤러를 위의 방식으로 띄우고 싶다면 storyboard
에 새로운 값을 넣을 필요 없이 storyboard
키워드만 작성해서 instantiateViewController(withReuseIdentifier:)
함수를 사용하고, present
에 넘겨주면 된다.
// header 부분
datasource.supplementaryViewProvider = {(collectionView, kind, indexPath) in
guard let header = collectionView.dequeueReusableSupplementaryView(ofKind:
UICollectionView.elementKindSectionHeader, withReuseIdentifier: "QuickFocusHeaderView", for: indexPath) as? QuickFocusHeaderView else {
return nil
}
let allSections = Section.allCases
let section = allSections[indexPath.section]
// section이라는 enum에 읽기 전용 프로퍼티인 title을 넘기는 모습.
header.configure(section.title)
return header
}
// Header 사이즈 구현
let headerSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .absolute(50))
let header = NSCollectionLayoutBoundarySupplementaryItem(layoutSize: headerSize,
elementKind: UICollectionView.elementKindSectionHeader, alignment: .top)
section.boundarySupplementaryItems = [header]
위 코드처럼 dequeueReusableSupplementaryView
랑 UICollectionView.elementKindSectionHeader
를 사용하는것의 차이를 빼면 cell에서 사용하던 dequeueReusableCell
과 거의 동일하다.
먼저 스토리보드에 Collection Reusable View
를 추가해주고 뷰 컨트롤러를 하나 만들어서 class, Identifier
에 연결을 해주고 위의 코드를 작성하면 된다
boundarySupplementaryItems
는 헤더와 푸터 같은 섹션의 경계 가장자리와 여녁ㄹ된 추가적인 항목의 배열이다.
스토리보드에서 적용하려면 아래 사진에서 Prefers Large Titles
를 체크하면 된다.
override func viewDidLoad() {
super.viewDidLoad()
// 적용하지 않으려면, 이 코드를 적어 주면 된다
self.navigationItem.largeTitleDisplayMode = .never
}