티스토리 뷰
vertical scrollView에 이어 horizontal scrollView도 만들어볼게욧!
오늘도 뭉치 모델이 고생해주었습니당.
지난 번에 만들어놓은 vertical 버튼과 horizontal 버튼 중 horizontal 버튼을 누르면 나오도록 해볼게욧
버튼 있는 뷰컨의 코드는 숨겨놓슴돠
import UIKit
import SnapKit
class ViewController : UIViewController {
let verticalButton = UIButton()
let horizontalButton = UIButton()
override func viewDidLoad() {
super.viewDidLoad()
setUI()
setDetail()
}
@objc func verticalBtnTapped(_ sender: UIButton) {
let nextVC = VerticalViewController()
self.navigationController?.pushViewController(nextVC, animated: true)
}
@objc func horizontalBtnTapped(_ sender: UIButton) {
let nextVC = HorizontalViewController()
self.navigationController?.pushViewController(nextVC, animated: true)
}
}
extension ViewController {
func setUI() {
[verticalButton, horizontalButton].forEach {
view.addSubview($0)
}
verticalButton.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.top.equalToSuperview().offset(400)
make.leading.trailing.equalToSuperview().inset(16)
}
horizontalButton.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.top.equalTo(verticalButton.snp.bottom).offset(50)
make.leading.trailing.equalToSuperview().inset(16)
}
}
func setDetail() {
[verticalButton, horizontalButton].forEach {
$0.setTitleColor(.white, for: .normal)
verticalButton.backgroundColor = .black
verticalButton.addTarget(self, action: #selector(verticalBtnTapped(_:)), for: .touchUpInside)
verticalButton.setTitle("Vertical", for: .normal)
horizontalButton.backgroundColor = .blue
horizontalButton.addTarget(self, action: #selector(horizontalBtnTapped(_:)), for: .touchUpInside)
horizontalButton.setTitle("horizontal", for: .normal)
}
}
}
스크롤뷰, 버튼, 레이블 등을 선언해주고요.
class HorizontalViewController: UIViewController {
let scrollView = UIScrollView()
let contentView = UIView()
let firstLabel = UILabel()
let secondLabel = UILabel()
let thirdLabel = UILabel()
let firstImageView = UIImageView()
let secondImageView = UIImageView()
let thirdImageView = UIImageView()
let pageControl = UIPageControl()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
configureUI()
// Do any additional setup after loading the view.
}
}
UI는 extension에서 잡을게요.
//MARK: -UI
extension HorizontalViewController {
final private func configureUI() {
setAttributes()
setScrollView()
setConstraints()
setPageControl()
}
final private func setAttributes() {
firstLabel.text = "측면 뭉치"
secondLabel.text = "정면 뭉치"
thirdLabel.text = "서 있는 뭉치"
[firstImageView, secondImageView, thirdImageView].forEach {
$0.contentMode = .scaleAspectFit
firstImageView.image = UIImage(named: "뭉치1")
secondImageView.image = UIImage(named: "뭉치2")
thirdImageView.image = UIImage(named: "뭉치3")
}
}
final private func setScrollView() {
scrollView.isPagingEnabled = true
scrollView.showsHorizontalScrollIndicator = false
scrollView.delegate = self
}
final func setPageControl() {
pageControl.numberOfPages = 3
pageControl.currentPage = 0
pageControl.currentPageIndicatorTintColor = .darkGray
pageControl.pageIndicatorTintColor = .lightGray
}
final private func setConstraints() {
view.addSubview(scrollView)
view.addSubview(pageControl)
scrollView.addSubview(contentView)
[firstLabel, secondLabel, thirdLabel, firstImageView, secondImageView, thirdImageView].forEach {
contentView.addSubview($0)
}
scrollView.snp.makeConstraints { make in
make.leading.trailing.equalToSuperview()
make.top.equalToSuperview().inset(100)
make.bottom.equalToSuperview().inset(300)
}
pageControl.snp.makeConstraints { make in
make.top.equalTo(scrollView.snp.bottom)
make.centerX.equalToSuperview()
}
let width = UIScreen.main.bounds.width
contentView.snp.makeConstraints { make in
make.edges.equalTo(scrollView.contentLayoutGuide)
make.height.equalTo(scrollView.frameLayoutGuide)
make.width.equalTo(width * 3)
}
firstImageView.snp.makeConstraints { make in
make.leading.equalTo(contentView)
make.bottom.equalTo(contentView)
make.height.equalTo(300)
make.width.equalTo(width)
}
firstLabel.snp.makeConstraints { make in
make.centerX.equalTo(firstImageView)
make.top.equalTo(contentView).offset(50)
}
secondImageView.snp.makeConstraints { make in
make.leading.equalTo(firstImageView.snp.trailing)
make.bottom.equalTo(contentView)
make.height.width.equalTo(firstImageView)
}
secondLabel.snp.makeConstraints { make in
make.centerX.equalTo(secondImageView)
make.top.equalTo(contentView).offset(50)
}
thirdImageView.snp.makeConstraints { make in
make.leading.equalTo(secondImageView.snp.trailing)
make.bottom.equalTo(contentView)
make.trailing.equalTo(contentView)
make.height.width.equalTo(firstImageView)
}
thirdLabel.snp.makeConstraints { make in
make.centerX.equalTo(thirdImageView)
make.top.equalTo(contentView).offset(50)
}
}
}
1. setConstraints()
(1) scrollView 레이아웃 잡기
- horizontal scrollView는 view보다 조금 작게 잡아줬구여.- contentView는 contentLayoutGuide와 같게 잡되, 오늘은 horizontal scrollView이기 때문에 높이를 frameLayoutGuide와 맞춰 주었습니당. 그리고 사진을 3개 넣을 예정이라 width는 스크린크기의 width에 3배를 주었어용.- 나머지 imageView와 label 들을 contentView에 addSubView하여 적절하게 배치했어여. 이미지뷰들의 width는 스크린 크기와 똑같이 해주었고, 높이는 대략 지정해주었쥬.
2. setPageControl()pageControl도 같이 넣어봤는데여, 기본적인 설정(전체 페이지 수, 현재 페이지 수, 컬러 등) 설정을 해두었어여.
3. setScrollView()scrollView도 많은 속성들이 있더라구요. HorizontalScrollIndicator가 안보였으면 해서 false로 설정했구여, paging이 됐으면 해서 true로 설정했습니당!그리고 pageControl에 해당 페이지가 표시되기 위해 scrollView Delegate를 사용할 예정이라, delegate=self 설정도 해주었습죠
이렇게 기본적인 셋팅은 끝!이구요.이제 사진을 넘기면 pageControl의 indicator도 변경되도록 해볼게욧
위에서 했던 delegate=self 설정을 꼭 해주시고, 해당 뷰컨(horizontal ViewController)를 확장(extension)하여 ScrollViewDelegate protocol을 채택해유
메서드 이름이 너무 직관적이네여. 스크롤뷰가 스크롤하면 불리는 메서드에여
그리고 pageControl의 currentPage를 설정해줍니다.
extension HorizontalViewController: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let currentPage = Int(scrollView.contentOffset.x/scrollView.frame.size.width)
pageControl.currentPage = currentPage
}
}
contentOffset은 scrollView origin에서 contentView의 origin의 거리를 얘기해요.
그래서 print로 scrollView.contentOffSet을 찍어보면
(1)번 사진 contentOffSet
- offSet: 0.0
- width: 0.0
(2)번 사진 contentOffSet
- offSet: 375.0
- width: 375.0
(2)번 사진 contentOffSet
- offSet: 750.0
- width: 375.0
이렇게 나와요. 그러니까 아래처럼 되는거져!
그러면 contentOffset을 width로 나누면 pageControl을 구할 수 있겠져?
근데 이렇게 실행하면 아래와 같은 에러가 발생하는 거에여?
print를 찍어보니, 아래처럼 nan이라는 값이 나오더라구여?
이럴 때는 마음의 고향 스택오버플로우로 가봅니당.
나눈 값을 round처리하거나, 두 값을 Float로 바꾸라거나의 해결책이 들지 않았어여.
그래서 엄청나게 검색하여 찾은 코드가 이거고, 이게 적용되는데 문제는 그 검색 페이지를 잃어버렸고, 아래 fmod라는 함수?는 공식문서에도 안 나오네여 ㅠㅠ
써도 괜찮은건가?ㅠㅠ
일단 아래처럼 작성하면, 사진은 잘 넘어갑니다!!
extension HorizontalViewController: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
print("offSet: \(scrollView.contentOffset.x)")
print("width: \(scrollView.frame.size.width)")
print(scrollView.contentOffset.x/scrollView.frame.size.width)
print(fmod(scrollView.contentOffset.x, scrollView.frame.size.width))
if fmod(scrollView.contentOffset.x, scrollView.frame.size.width) == 0 {
let currentPage = Int(scrollView.contentOffset.x/scrollView.frame.size.width)
pageControl.currentPage = currentPage
}
}
}
찜찜한 마음으로 마무리하며,,, 전체코드는 아래에 둘게여,,,
import UIKit
import SnapKit
class HorizontalViewController: UIViewController {
let scrollView = UIScrollView()
let contentView = UIView()
let firstLabel = UILabel()
let secondLabel = UILabel()
let thirdLabel = UILabel()
let firstImageView = UIImageView()
let secondImageView = UIImageView()
let thirdImageView = UIImageView()
let pageControl = UIPageControl()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
configureUI()
}
}
extension HorizontalViewController: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
print("offSet: \(scrollView.contentOffset.x)")
print("width: \(scrollView.frame.size.width)")
print(scrollView.contentOffset.x/scrollView.frame.size.width)
print(fmod(scrollView.contentOffset.x, scrollView.frame.size.width))
if fmod(scrollView.contentOffset.x, scrollView.frame.size.width) == 0 {
let currentPage = Int(scrollView.contentOffset.x/scrollView.frame.size.width)
pageControl.currentPage = currentPage
}
}
}
//MARK: -UI
extension HorizontalViewController {
final private func configureUI() {
setAttributes()
setScrollView()
setConstraints()
setPageControl()
}
final private func setAttributes() {
firstLabel.text = "측면 뭉치"
secondLabel.text = "정면 뭉치"
thirdLabel.text = "서 있는 뭉치"
[firstImageView, secondImageView, thirdImageView].forEach {
$0.contentMode = .scaleAspectFit
firstImageView.image = UIImage(named: "뭉치1")
secondImageView.image = UIImage(named: "뭉치2")
thirdImageView.image = UIImage(named: "뭉치3")
}
}
final private func setScrollView() {
scrollView.isPagingEnabled = true
scrollView.showsHorizontalScrollIndicator = false
scrollView.delegate = self
}
final func setPageControl() {
pageControl.numberOfPages = 3
pageControl.currentPage = 0
pageControl.currentPageIndicatorTintColor = .darkGray
pageControl.pageIndicatorTintColor = .lightGray
}
final private func setConstraints() {
view.addSubview(scrollView)
view.addSubview(pageControl)
scrollView.addSubview(contentView)
[firstLabel, secondLabel, thirdLabel, firstImageView, secondImageView, thirdImageView].forEach {
contentView.addSubview($0)
}
scrollView.snp.makeConstraints { make in
make.leading.trailing.equalToSuperview()
make.top.equalToSuperview().inset(100)
make.bottom.equalToSuperview().inset(300)
}
pageControl.snp.makeConstraints { make in
make.top.equalTo(scrollView.snp.bottom)
make.centerX.equalToSuperview()
}
let width = UIScreen.main.bounds.width
contentView.snp.makeConstraints { make in
make.edges.equalTo(scrollView.contentLayoutGuide)
make.height.equalTo(scrollView.frameLayoutGuide)
make.width.equalTo(width * 3)
}
firstImageView.snp.makeConstraints { make in
make.leading.equalTo(contentView)
make.bottom.equalTo(contentView)
make.height.equalTo(300)
make.width.equalTo(width)
}
firstLabel.snp.makeConstraints { make in
make.centerX.equalTo(firstImageView)
make.top.equalTo(contentView).offset(50)
}
secondImageView.snp.makeConstraints { make in
make.leading.equalTo(firstImageView.snp.trailing)
make.bottom.equalTo(contentView)
make.height.width.equalTo(firstImageView)
}
secondLabel.snp.makeConstraints { make in
make.centerX.equalTo(secondImageView)
make.top.equalTo(contentView).offset(50)
}
thirdImageView.snp.makeConstraints { make in
make.leading.equalTo(secondImageView.snp.trailing)
make.bottom.equalTo(contentView)
make.trailing.equalTo(contentView)
make.height.width.equalTo(firstImageView)
}
thirdLabel.snp.makeConstraints { make in
make.centerX.equalTo(thirdImageView)
make.top.equalTo(contentView).offset(50)
}
}
}
근데 생각해보니, 왜 이미지뷰를 3개나 썼을까요?
그냥 이미지 배열에서 불러오면 되는데?
글을 쓰는 이 와중에 드는 생각이네여,,,ㅎ
다음에는 그러면 이미지뷰 하나로 배열에 이미지 담아서,,,나 샛기,,,,
https://developer.apple.com/documentation/uikit/uiscrollviewdelegate/1619392-scrollviewdidscroll
Apple Developer Documentation
developer.apple.com
https://developer.apple.com/documentation/uikit/uiscrollview/1619404-contentoffset/
Apple Developer Documentation
developer.apple.com
'iOS 개발자 되기' 카테고리의 다른 글
[swift] 코드로 horizontal scrollView 만들어보기 번외편 (2) | 2022.07.28 |
---|---|
[swift] 코드로 vertical scrollView 만들어보기 2 (0) | 2022.07.05 |
[swift] 코드로 vertical scrollView 만들어보기 (0) | 2022.06.28 |
[swift] SwiftLint 적용해보기 (brew 이용) (0) | 2022.04.13 |
[swift] 1. 랜덤 명언 생성기 만들기 (programmatically) (0) | 2022.03.17 |
- Total
- Today
- Yesterday