Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
Tags
- map
- combine
- ios
- clean architecture
- Refactoring
- Observable
- collectionview
- SWIFT
- swift documentation
- uitableview
- tableView
- UICollectionView
- Protocol
- Human interface guide
- Xcode
- 리펙터링
- MVVM
- uiscrollview
- 리팩토링
- rxswift
- UITextView
- 스위프트
- 리펙토링
- Clean Code
- 애니메이션
- RxCocoa
- 클린 코드
- ribs
- HIG
- swiftUI
Archives
- Today
- Total
김종권의 iOS 앱 개발 알아가기
[iOS - swift] 2. UISegmentedControl - 커스텀 방법, PageViewController와 사용 방법 본문
iOS 응용 (swift)
[iOS - swift] 2. UISegmentedControl - 커스텀 방법, PageViewController와 사용 방법
jake-kim 2022. 5. 6. 23:111. UISegmentedControl - 기본 사용 방법
2. UISegmentedControl - 커스텀 방법, PageViewController와 사용 방법
UISegmentedControl 커스텀 방법
- 클래스 준비
import UIKit
final class UnderlineSegmentedControl: UISegmentedControl {
}
- UISegementedControl은 아래와 같이 배경색과 divider가 존재
- 회색 배경과 divider를 지우는 코드 추가
override init(frame: CGRect) {
super.init(frame: frame)
self.removeBackgroundAndDivider()
}
override init(items: [Any]?) {
super.init(items: items)
self.removeBackgroundAndDivider()
}
required init?(coder: NSCoder) {
fatalError()
}
private func removeBackgroundAndDivider() {
let image = UIImage()
self.setBackgroundImage(image, for: .normal, barMetrics: .default)
self.setBackgroundImage(image, for: .selected, barMetrics: .default)
self.setBackgroundImage(image, for: .highlighted, barMetrics: .default)
self.setDividerImage(image, forLeftSegmentState: .selected, rightSegmentState: .normal, barMetrics: .default)
}
- underlineView 추가
- autolayout이 아닌, frame을 이용하므로 layoutSubviews에서 호출
private lazy var underlineView: UIView = {
let width = self.bounds.size.width / CGFloat(self.numberOfSegments)
let height = 2.0
let xPosition = CGFloat(self.selectedSegmentIndex * Int(width))
let yPosition = self.bounds.size.height - 1.0
let frame = CGRect(x: xPosition, y: yPosition, width: width, height: height)
let view = UIView(frame: frame)
view.backgroundColor = .green
self.addSubview(view)
return view
}()
override func layoutSubviews() {
super.layoutSubviews()
let underlineFinalXPosition = (self.bounds.width / CGFloat(self.numberOfSegments)) * CGFloat(self.selectedSegmentIndex)
UIView.animate(
withDuration: 0.1,
animations: {
self.underlineView.frame.origin.x = underlineFinalXPosition
}
)
}
* 초록색은 문구 지정 방법은 사용하는쪽에서 설정
// ViewController.swift
self.segmentedControl.setTitleTextAttributes([NSAttributedString.Key.foregroundColor: UIColor.gray], for: .normal)
self.segmentedControl.setTitleTextAttributes(
[
NSAttributedString.Key.foregroundColor: UIColor.green,
.font: UIFont.systemFont(ofSize: 13, weight: .semibold)
],
for: .selected
)
self.segmentedControl.selectedSegmentIndex = 0
ViewController에서 PageViewController와 결합
- PageViewController와 예제로 사용할 VC 3개와 관련 프로퍼티 준비
- pageViewController 개념은 이전 포스팅 글 참고
- dataViewContoller 프로퍼티: pageViewController의 델리게이트에서 dataSource로 사용할 값
- currentPage 프로퍼티: segmentedControl에서 값이 변경될때 해당 프로퍼티를 업데이트하여, didSet에서 pageViewController에도 업데이트 시켜줄 용도
// ViewController.swift
private let vc1: UIViewController = {
let vc = UIViewController()
vc.view.backgroundColor = .red
return vc
}()
private let vc2: UIViewController = {
let vc = UIViewController()
vc.view.backgroundColor = .green
return vc
}()
private let vc3: UIViewController = {
let vc = UIViewController()
vc.view.backgroundColor = .blue
return vc
}()
private lazy var pageViewController: UIPageViewController = {
let vc = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil)
vc.setViewControllers([self.dataViewControllers[0]], direction: .forward, animated: true)
vc.delegate = self
vc.dataSource = self
vc.view.translatesAutoresizingMaskIntoConstraints = false
return vc
}()
var dataViewControllers: [UIViewController] {
[self.vc1, self.vc2, self.vc3]
}
var currentPage: Int = 0 {
didSet {
// from segmentedControl -> pageViewController 업데이트
print(oldValue, self.currentPage)
let direction: UIPageViewController.NavigationDirection = oldValue <= self.currentPage ? .forward : .reverse
self.pageViewController.setViewControllers(
[dataViewControllers[self.currentPage]],
direction: direction,
animated: true,
completion: nil
)
}
}
- segmentedControl 값이 변경될 때 pageViewController에도 적용시켜주기 위해서 selector 추가
// in viewDidLoad()
self.changeValue(control: self.segmentedControl)
@objc private func changeValue(control: UISegmentedControl) {
self.currentPage = control.selectedSegmentIndex
}
- pageViewController 데이터 소스 처리
// pageViewController.dataSource = self
extension ViewController: UIPageViewControllerDataSource {
func pageViewController(
_ pageViewController: UIPageViewController,
viewControllerBefore viewController: UIViewController
) -> UIViewController? {
guard
let index = self.dataViewControllers.firstIndex(of: viewController),
index - 1 >= 0
else { return nil }
return self.dataViewControllers[index - 1]
}
func pageViewController(
_ pageViewController: UIPageViewController,
viewControllerAfter viewController: UIViewController
) -> UIViewController? {
guard
let index = self.dataViewControllers.firstIndex(of: viewController),
index + 1 < self.dataViewControllers.count
else { return nil }
return self.dataViewControllers[index + 1]
}
}
- pageViewController에서 값이 변경될 때 segmentedControl에도 적용하기 위해, delegate 처리
- 위 dataSource에서 처리하면 캐싱이 되어 index값이 모두 불리지 않으므로, delegate에서 따로 처리가 필요
extension ViewController: UIPageViewControllerDelegate {
func pageViewController(
_ pageViewController: UIPageViewController,
didFinishAnimating finished: Bool,
previousViewControllers: [UIViewController],
transitionCompleted completed: Bool
) {
guard
let viewController = pageViewController.viewControllers?[0],
let index = self.dataViewControllers.firstIndex(of: viewController)
else { return }
self.currentPage = index
self.segmentedControl.selectedSegmentIndex = index
}
}
* 전체 코드: https://github.com/JK0369/ExCustomSegmentedControl
* 참고
'iOS 응용 (swift)' 카테고리의 다른 글
Comments