CardViewModel

기존 하나의 유저당 하나의 사진에서 유저 한명 당 여러개의 사진을 넣을 수 있도록 한다.

So, CardViewModel의 imageName을 String array로 변경한다.

현재 Model을 생성하여 toCardViewModel() 메소드를 통해 CardViewModel로 바꾼 뒤에 HomeController에 띄울 때 CardView로 전환하여 subview로 넣는다.

기존의 CardViewModel에서는 imageName으로 하나의 이미지 이름만 받았는데 여기서 imageNames로 array로 변경한다.

let imageName = cardViewModel.imageNames[0] // bad access
let imageName = cardViewModel.imageNames.first ?? "" // safe way

array의 첫번째 값을 인덱스로 0으로 하드하게 가져오는 것보다 안전한 방법인 first를 사용한다. 여기서 Nil-Coalescing Operator 사용하는데 이 operator는 앞의 값이 nil이라면 operator 뒤의 값을 반환한다. first와 Nil-Coalescing Operator를 사용하는 이유는 imageNames.count가 0일 때 index 0에 접근하려고 하면 crash가 발생한다.

barsStackView for user’s images cycle

cardView에서 horizontal bar를 만들자

setupLayout()에서 imageView를 add subview 한 뒤에 추가해준다.

fileprivate let barsStackView = UIStackView()
    
    fileprivate func setupBarsStackView() {
        addSubview(barsStackView)
        barsStackView.anchor(top: topAnchor, leading: leadingAnchor, bottom: nil, trailing: trailingAnchor, padding: .init(top: 8, left: 8, bottom: 0, right: 8), size: .init(width: 0, height: 4))
        barsStackView.spacing = 4
        barsStackView.distribution = .fillEqually
        // some dummy bars for now
        (0..<10).forEach { (_) in
            let barView = UIView()
            barView.backgroundColor = .white
            barsStackView.addArrangedSubview(barView)
        }
    }

barStackView.spacing 으로 스택마다 간격을 준다.

stackView의 요소들은 친숙해지자. addArrangedSubview()는 필수이다.

	barView.backgroundColor = UIColor(white: 0, alpha: 1)

UIColor(white: 0)을 주면 이것은 검정색이다. 그리고 alpha값은 1이면 불투명이다.

	barsStackView.arrangedSubviews.first?.backgroundColor = .white

시작할 때 처음 index 표시

// CardView.swift
var cardViewModel: CardViewModel! {
        didSet {
            // accessing index 0 will crash if imageNames.count == 0
            let imageName = cardViewModel.imageNames.first ?? ""
            imageView.image = UIImage(named: imageName)
            informationLabel.attributedText = cardViewModel.attributedString
            informationLabel.textAlignment = cardViewModel.textAlignment
            
            (0..<cardViewModel.imageNames.count).forEach { (_) in
                let barView = UIView()
                barView.backgroundColor = UIColor(white: 0, alpha: 0.1)
                barsStackView.addArrangedSubview(barView)
            }
            barsStackView.arrangedSubviews.first?.backgroundColor = .white
        }
    }

CardView에서 didSet에서 (cardViewModel에 접근할 수 있어서) imageNames.count 만큼 개수를 추가해준다.

여기서 user들은 이제 자신의 사진들을 여러개 가지게 되기 때문에 refactoring 해준다.

// User.swift
let imageNames: [String]

위와 같이 refactoring해준다. User만 하고 Advertiser는 하지 않는다. 그러면 advertiser는 항상 하나만 갖는다.

UITapGestureRecognizer

UIPanGesture와 동일하게 Recognizer를 만들고 addGestureRecognizer()에 넣는다.

addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleTap)))

코드 단축

shouldAdvanceNextPhoto라는 bool 변수를 만들어준다.

tapLocation과 ?를 이용하여 true, false 판별

let tapLocation = gesture.location(in: nil)

location(_) Parameters

  • view

    A UIView object on which the gesture took place. Specify nil to indicate the window.

let과 var의 차이는 확실하게 알아두자. const도 함께 알아두면 더욱 좋다. let 관련 정리 사이트

    var imageIndex = 0
    
    @objc fileprivate func handleTap(gesture: UITapGestureRecognizer) {
        print("Handling tap and cycling photos")
        let tapLocation = gesture.location(in: nil)
        let shouldAdvanceNextPhoto = tapLocation.x > frame.width / 2 ? true : false
        if shouldAdvanceNextPhoto {
            imageIndex = imageIndex + 1
        } else {
            imageIndex = imageIndex - 1
        }
        barsStackView.arrangedSubviews[imageIndex].backgroundColor = .white
        imageView.image = UIImage(named: cardViewModel.imageNames[imageIndex])
    }

imageIndex를 handleTap 안에 선언해버리면 계속 0부터 시작해서 안된다.

계속 Tap을 해버리면 imageIndex는 overflow를 일으켜 버린다.

if shouldAdvanceNextPhoto {
            imageIndex = min(imageIndex + 1, cardViewModel.imageNames.count - 1)
        } else {
            imageIndex = max(imageIndex - 1, 0)
        }
        let imageName = cardViewModel.imageNames[imageIndex]
        imageView.image = UIImage(named: imageName)

최대 최소는 min, max 메소드를 이용하여 처리한다.

barsStackView.arrangedSubviews.forEach { (v) in
            v.backgroundColor = UIColor(white: 0, alpha: 0.1)
        }
        barsStackView.arrangedSubviews[imageIndex].backgroundColor = .white

여러개 중에 하나만 탭하는 경우는 모두를 초기화 하고 선택한 하나를 마지막에 업데이트 해준다.

그러면 정확한 이미지의 인덱스만 색깔을 바꿔줄 수 있다.

fileprivate let DeselectedBarColor = UIColor(white: 0, alpha: 0.1)
..
v.backgroundColor = DeselectedBarColor
..

다음과 같이 추상화 작업을 해준다. 나중에 색을 바꿀 때 여기서만 바꾸면 되기 때문이다.

코드의 추상화, 재활용성을 항상 생각하자

태그:

카테고리:

업데이트:

댓글남기기