티스토리 뷰
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
}
}
}
참고한 자료및 문서
'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 |