티스토리 뷰

iOS/iOS-Memo

Underline SegmanetControl

malrang-malrang 2022. 8. 12. 16:45

Underline SegmanetControl

포폴용으로 마켓 앱을 만들고 있는데 오늘의집의 앱의 상단에 위치한
누르면 underline이 움직이는 요거 만들고 싶었습니다.

먼저 SegmentControl을 상속받는 CustomSegmentControl 을 만들어줍니다!

private enum SegmentType: String, CaseIterable {
    case latelyProduct = "최근 상품"
    case popularProduct = "인기 상품"

    static var value: [String] {
        return Self.allCases.map { $0.rawValue }
    }
}

final class UnderlineSegmentControl: UISegmentedControl {
    init() {
        super.init(items: SegmentType.value)
        self.setupSegmentControl()
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    private func setupSegmentControl() {
        self.translatesAutoresizingMaskIntoConstraints = false
        self.selectedSegmentIndex = 0
        let selectedFontColor = [NSAttributedString.Key.foregroundColor: #colorLiteral(red: 1, green: 0.7698566914, blue: 0.8562441468, alpha: 1)]
        self.setTitleTextAttributes(selectedFontColor, for: .selected)
        let font = UIFont.preferredFont(forTextStyle: .subheadline)
        self.setTitleTextAttributes([NSAttributedString.Key.font: font], for: .normal)
    }
}

요렇게 만들게되면 SegmentControl 에 구분선이랑 배경이 남게된다..!

 private func removeBackgroundAndDivider() {
     //Selcted 되지 않은 녀석의 배경을 제거합니다!
        self.setBackgroundImage(UIImage(), for: .normal, barMetrics: .default)
     //Selected 된 녀석의 배경을 제거합니다!
        self.setBackgroundImage(UIImage(), for: .selected, barMetrics: .default)
     //강조? 효과가 발생한 녀석의 배경을 제거합니다!
        self.setBackgroundImage(UIImage(), for: .highlighted, barMetrics: .default)
     //SegmentControl Item의 구분선을 제거합니다!
        self.setDividerImage(UIImage(), forLeftSegmentState: .selected, rightSegmentState: .normal, barMetrics: .default)
    }

요런 메서드를 만들어서 구분선과 배경을 제거해줍시다!!
적용 하게되면! (위의 메서드는 init() 내부에서 호출했습니다!)

이렇게 배경과 구분선이 제거된걸 확인할수 있습니다!

다음으로는 SegmentControl을 Select했을때 underline이 움직이는걸 구현해야합니다!

private let underlineView: UIView = {
        let view = UIView()
        view.backgroundColor = #colorLiteral(red: 1, green: 0.7698566914, blue: 0.8562441468, alpha: 1)
        view.layer.cornerRadius = 2.5
        return view
    }()

요런 녀석을 하나 만들어줍시다! 요녀석이 선택된 index 밑에 표시될 분홍색 underline입니다!

그런다음에!!

func changeUnderlinePosition() {
        let width = self.bounds.size.width / (CGFloat(self.numberOfSegments) * 2)
        let height = 7.0
        let spacer = (self.bounds.size.width / (CGFloat(self.numberOfSegments) * 4))
        let dividePotin = self.bounds.width / CGFloat(self.numberOfSegments)
        let xPosition = CGFloat(self.selectedSegmentIndex) * dividePotin + spacer
        let yPosition = self.bounds.size.height - height
        let frame = CGRect(x: xPosition, y: yPosition, width: width, height: height)
        UIView.animate(withDuration: 0.1) {
            self.underlineView.frame = frame
        }
    }

요런 메서드를 layoutSubviews() 내부에서 호출해줍니다!
layoutSubViews()가 호출될때마다 underline의 위치가 변경됩니다!

그러면 요렇게 만들고자 했던 녀석을 만들수 있습니다!

아래의 회색 구분선은 View 입니다!

final class SegmentView: UIView {
    private let segmentControl = UnderlineSegmentControl()

    init(viewModel: MainViewModelable) {
        self.viewModel = viewModel
        super.init(frame: CGRect())
        self.setupView()
        self.setupConstraint()
        self.bind()
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    private func setupView() {
        self.translatesAutoresizingMaskIntoConstraints = false
        self.backgroundColor = .systemBackground
        self.addSubviews(self.segmentControl, self.underlineView)
    }

    private func setupConstraint() {
        self.segmentControl.snp.makeConstraints {
            $0.top.leading.trailing.equalToSuperview()
            $0.height.equalToSuperview().multipliedBy(0.98)
        }

        self.underlineView.snp.makeConstraints {
            $0.top.equalTo(self.segmentControl.snp.bottom)
            $0.leading.trailing.bottom.equalToSuperview()
        }
    }
}

요렇게 SegmentControl을 소유하고있는 뷰를 만들어서 내부에 underlineView 로 경계선을 만들어줬어요!

전체코드

final class SegmentView: UIView {
    private let segmentControl = UnderlineSegmentControl()

    init(viewModel: MainViewModelable) {
        self.viewModel = viewModel
        super.init(frame: CGRect())
        self.setupView()
        self.setupConstraint()
        self.bind()
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    private func setupView() {
        self.translatesAutoresizingMaskIntoConstraints = false
        self.backgroundColor = .systemBackground
        self.addSubviews(self.segmentControl, self.underlineView)
    }

    private func setupConstraint() {
        self.segmentControl.snp.makeConstraints {
            $0.top.leading.trailing.equalToSuperview()
            $0.height.equalToSuperview().multipliedBy(0.98)
        }

        self.underlineView.snp.makeConstraints {
            $0.top.equalTo(self.segmentControl.snp.bottom)
            $0.leading.trailing.bottom.equalToSuperview()
        }
    }
}

enum Page: Int, CaseIterable {
    case latelyProduct = 0
    case popularProduct = 1
}

extension Page {
    static var inventory: [String] {
        return Self.allCases.map { $0.description }
    }

    var value: Int {
        return self.rawValue
    }

    private var description: String {
        switch self {
        case .latelyProduct:
            return "최근 상품"
        case .popularProduct:
            return "인기 상품"
        }
    }
}

final class UnderlineSegmentControl: UISegmentedControl {
    private let underlineView: UIView = {
        let view = UIView()
        view.backgroundColor = #colorLiteral(red: 1, green: 0.7698566914, blue: 0.8562441468, alpha: 1)
        view.layer.cornerRadius = 2.5
        return view
    }()

    init() {
        super.init(items: SegmentType.inventory)
        self.setupSegmentControl()
        self.removeBackgroundAndDivider()
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        self.changeUnderlinePosition()
    }

    private func setupSegmentControl() {
        self.addSubview(underlineView)
        self.translatesAutoresizingMaskIntoConstraints = false
        let selectedFontColor = [NSAttributedString.Key.foregroundColor: #colorLiteral(red: 1, green: 0.7698566914, blue: 0.8562441468, alpha: 1)]
        self.setTitleTextAttributes(selectedFontColor, for: .selected)
        let font = UIFont.preferredFont(forTextStyle: .subheadline)
        self.setTitleTextAttributes([NSAttributedString.Key.font: font], for: .normal)
    }

    private func removeBackgroundAndDivider() {
        self.setBackgroundImage(UIImage(), for: .normal, barMetrics: .default)
        self.setBackgroundImage(UIImage(), for: .selected, barMetrics: .default)
        self.setBackgroundImage(UIImage(), for: .highlighted, barMetrics: .default)
        self.setDividerImage(UIImage(), forLeftSegmentState: .selected, rightSegmentState: .normal, barMetrics: .default)
    }

    func changeUnderlinePosition() {
        let width = self.bounds.size.width / (CGFloat(self.numberOfSegments) * 2)
        let height = 7.0
        let spacer = (self.bounds.size.width / (CGFloat(self.numberOfSegments) * 4))
        let dividePotin = self.bounds.width / CGFloat(self.numberOfSegments)
        let xPosition = CGFloat(self.selectedSegmentIndex) * dividePotin + spacer
        let yPosition = self.bounds.size.height - height
        let frame = CGRect(x: xPosition, y: yPosition, width: width, height: height)
        UIView.animate(withDuration: 0.1) {
            self.underlineView.frame = frame
        }
    }
}

참고한 자료및 문서

https://ios-development.tistory.com/963?category=899471

'iOS > iOS-Memo' 카테고리의 다른 글

APIKey를 숨겨보자!  (0) 2022.10.09
Cache, NSCache, URLCache  (1) 2022.09.30
NotificationCenter, Modal 사용시 주의점!  (0) 2022.04.29
tag 사용법  (0) 2022.04.29
UserDefaults  (0) 2022.04.29
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/07   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31
글 보관함