parent
36d13b2769
commit
5604910471
@ -0,0 +1,639 @@
|
|||||||
|
//
|
||||||
|
// SegmentControl.swift
|
||||||
|
// IndieMusic
|
||||||
|
//
|
||||||
|
// Created by WenLei on 2023/11/15.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
struct SegmentControlSetting {
|
||||||
|
|
||||||
|
static let itemTextColor = UIColor.init(hex: 0xFFFFFF, alpha: 0.4)
|
||||||
|
static let itemSelectedTextColor = UIColor.init(hex: 0xFFFFFF, alpha: 1)
|
||||||
|
|
||||||
|
static let itemBackgroundColor = color(red: 255.0, green: 250.0, blue: 250.0, alpha: 1.0)
|
||||||
|
static let itemSelectedBackgroundColor = color(red: 255.0, green: 250.0, blue: 250.0, alpha: 1.0)
|
||||||
|
|
||||||
|
static let itemBorder : CGFloat = 30.0
|
||||||
|
//MARK - Text font
|
||||||
|
static let textFont = UIFont.systemFont(ofSize: 16.0)
|
||||||
|
static let selectedTextFont = UIFont.systemFont(ofSize: 16.0)
|
||||||
|
|
||||||
|
//MARK - slider
|
||||||
|
static let selectedViewBackgroundColor = UIColor.orange
|
||||||
|
static let selectedViewpadding : CGFloat = 20.0
|
||||||
|
|
||||||
|
//MARK - bridge
|
||||||
|
static let bridgeColor = UIColor.red
|
||||||
|
static let bridgeWidth : CGFloat = 7.0
|
||||||
|
|
||||||
|
//MARK - divider
|
||||||
|
static let dividerWidth : CGFloat = 2.0
|
||||||
|
static let dividerpPadding : CGFloat = 10.0
|
||||||
|
|
||||||
|
|
||||||
|
//MARK - inline func
|
||||||
|
@inline(__always) static func color(red:Float, green:Float, blue:Float, alpha:Float) -> UIColor {
|
||||||
|
return UIColor.init(red: CGFloat(red/255.0), green: CGFloat(green/255.0), blue: CGFloat(blue/255.0), alpha: CGFloat(alpha))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate class SelectedBackgroundView : UIView {
|
||||||
|
|
||||||
|
lazy var cornerMask: CAShapeLayer = {
|
||||||
|
let mask = CAShapeLayer()
|
||||||
|
return mask
|
||||||
|
}()
|
||||||
|
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
super.init(frame: frame)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override func layoutSubviews() {
|
||||||
|
super.layoutSubviews()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder aDecoder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate var color : UIColor? {
|
||||||
|
didSet{
|
||||||
|
self.backgroundColor = color
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate enum SegmentItemViewState : Int {
|
||||||
|
case Normal
|
||||||
|
case Selected
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate class SegmentItemView : UIView {
|
||||||
|
|
||||||
|
fileprivate func itemWidth() -> CGFloat {
|
||||||
|
|
||||||
|
if let text = titleLabel.text {
|
||||||
|
let string = text as NSString
|
||||||
|
let size = string.size(withAttributes: [NSAttributedString.Key.font:selectedFont!])
|
||||||
|
return size.width + SegmentControlSetting.itemBorder
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0.0
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate let titleLabel = UILabel()
|
||||||
|
fileprivate lazy var bridgeView : CALayer = {
|
||||||
|
let view = CALayer()
|
||||||
|
let width = SegmentControlSetting.bridgeWidth
|
||||||
|
view.bounds = CGRect(x: 0.0, y: 0.0, width: width, height: width)
|
||||||
|
view.backgroundColor = SegmentControlSetting.bridgeColor.cgColor
|
||||||
|
view.cornerRadius = view.bounds.size.width * 0.5
|
||||||
|
return view
|
||||||
|
}()
|
||||||
|
|
||||||
|
fileprivate lazy var dividerImageView : UIImageView = {
|
||||||
|
let dividerImageView = UIImageView.init(image: UIImage.init(named: ""))
|
||||||
|
dividerImageView.backgroundColor = .gray
|
||||||
|
|
||||||
|
return dividerImageView
|
||||||
|
}()
|
||||||
|
|
||||||
|
|
||||||
|
fileprivate var dividerWidth : CGFloat = 1 {
|
||||||
|
didSet{
|
||||||
|
if state == .Normal {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate var dividerpPadding : CGFloat = 13 {
|
||||||
|
didSet{
|
||||||
|
if state == .Normal {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fileprivate func showBridge(show:Bool){
|
||||||
|
self.bridgeView.isHidden = !show
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate var state : SegmentItemViewState = .Normal {
|
||||||
|
didSet{
|
||||||
|
updateItemView(state: state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate var font : UIFont?{
|
||||||
|
didSet{
|
||||||
|
if state == .Normal {
|
||||||
|
self.titleLabel.font = font
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fileprivate var selectedFont : UIFont?{
|
||||||
|
didSet{
|
||||||
|
if state == .Selected {
|
||||||
|
self.titleLabel.font = selectedFont
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate var text : String?{
|
||||||
|
didSet{
|
||||||
|
self.titleLabel.text = text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate var textColor : UIColor?{
|
||||||
|
didSet{
|
||||||
|
if state == .Normal {
|
||||||
|
self.titleLabel.textColor = textColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fileprivate var selectedTextColor : UIColor?{
|
||||||
|
didSet{
|
||||||
|
if state == .Selected {
|
||||||
|
self.titleLabel.textColor = selectedTextColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate var itemBackgroundColor : UIColor?{
|
||||||
|
didSet{
|
||||||
|
if state == .Normal {
|
||||||
|
self.backgroundColor = .red
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fileprivate var selectedBackgroundColor : UIColor?{
|
||||||
|
didSet{
|
||||||
|
if state == .Selected {
|
||||||
|
self.backgroundColor = selectedBackgroundColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate var textAlignment = NSTextAlignment.center {
|
||||||
|
didSet{
|
||||||
|
self.titleLabel.textAlignment = textAlignment
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func updateItemView(state: SegmentItemViewState){
|
||||||
|
switch state {
|
||||||
|
case .Normal:
|
||||||
|
self.titleLabel.font = self.font
|
||||||
|
self.titleLabel.textColor = self.textColor
|
||||||
|
self.backgroundColor = self.itemBackgroundColor
|
||||||
|
case .Selected:
|
||||||
|
self.titleLabel.font = selectedFont
|
||||||
|
self.titleLabel.textColor = self.selectedTextColor
|
||||||
|
self.backgroundColor = self.selectedBackgroundColor
|
||||||
|
}
|
||||||
|
self.setNeedsLayout()
|
||||||
|
self.layoutIfNeeded()
|
||||||
|
}
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
super.init(frame: frame)
|
||||||
|
|
||||||
|
|
||||||
|
titleLabel.textAlignment = .center
|
||||||
|
|
||||||
|
addSubview(titleLabel)
|
||||||
|
|
||||||
|
bridgeView.isHidden = true
|
||||||
|
layer.addSublayer(bridgeView)
|
||||||
|
|
||||||
|
layer.masksToBounds = true
|
||||||
|
|
||||||
|
addSubview(dividerImageView)
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate override func layoutSubviews() {
|
||||||
|
super.layoutSubviews()
|
||||||
|
|
||||||
|
backgroundColor = UIColor.clear
|
||||||
|
|
||||||
|
|
||||||
|
titleLabel.sizeToFit()
|
||||||
|
|
||||||
|
titleLabel.center.x = bounds.size.width * 0.5
|
||||||
|
titleLabel.center.y = bounds.size.height * 0.5
|
||||||
|
|
||||||
|
let width = bridgeView.bounds.size.width
|
||||||
|
let x:CGFloat = titleLabel.frame.maxX
|
||||||
|
bridgeView.frame = CGRect(x: x, y: bounds.midY - width, width: width, height: width)
|
||||||
|
|
||||||
|
dividerImageView.frame = CGRect(x: bounds.maxX - dividerWidth, y: bounds.minY + dividerpPadding, width: 1, height: bounds.size.height - dividerpPadding * 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder aDecoder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc public protocol SegmentControlDelegate {
|
||||||
|
@objc optional func didSelected(segement: SegmentControl, index: Int)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
open class SegmentControl: UIControl {
|
||||||
|
|
||||||
|
fileprivate struct Constants {
|
||||||
|
static let height : CGFloat = 40.0
|
||||||
|
}
|
||||||
|
|
||||||
|
open weak var delegate : SegmentControlDelegate?
|
||||||
|
|
||||||
|
open var autoAdjustWidth = false {
|
||||||
|
didSet{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
open func segementWidth() -> CGFloat {
|
||||||
|
return bounds.size.width / (CGFloat)(itemViews.count)
|
||||||
|
}
|
||||||
|
|
||||||
|
open func segmentWidth(index: Int) -> CGFloat {
|
||||||
|
guard index >= 0 && index < itemViews.count else {
|
||||||
|
return 0.0
|
||||||
|
}
|
||||||
|
if autoAdjustWidth {
|
||||||
|
return itemViews[index].itemWidth()
|
||||||
|
} else{
|
||||||
|
return segementWidth()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open func selectedViewWidth(index: Int) -> CGFloat {
|
||||||
|
guard index >= 0 && index < itemViews.count else {
|
||||||
|
return segmentWidth(index: index)
|
||||||
|
}
|
||||||
|
if autoAdjustWidth {
|
||||||
|
return itemViews[index].itemWidth() - SegmentControlSetting.selectedViewpadding
|
||||||
|
} else{
|
||||||
|
return segementWidth() - SegmentControlSetting.selectedViewpadding
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open func selectedViewHeight() -> CGFloat {
|
||||||
|
return 10
|
||||||
|
}
|
||||||
|
|
||||||
|
open var selectedIndex = 0 {
|
||||||
|
willSet{
|
||||||
|
let originItem = self.itemViews[selectedIndex]
|
||||||
|
originItem.state = .Normal
|
||||||
|
|
||||||
|
let selectItem = self.itemViews[newValue]
|
||||||
|
selectItem.state = .Selected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
open var itemTextColor = SegmentControlSetting.itemTextColor{
|
||||||
|
didSet{
|
||||||
|
self.itemViews.forEach { (itemView) in
|
||||||
|
itemView.textColor = itemTextColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open var itemSelectedTextColor = SegmentControlSetting.itemSelectedTextColor{
|
||||||
|
didSet{
|
||||||
|
self.itemViews.forEach { (itemView) in
|
||||||
|
itemView.selectedTextColor = itemSelectedTextColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
open var itemBackgroundColor = SegmentControlSetting.itemBackgroundColor{
|
||||||
|
didSet{
|
||||||
|
self.itemViews.forEach { (itemView) in
|
||||||
|
itemView.itemBackgroundColor = itemBackgroundColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open var itemSelectedBackgroundColor = SegmentControlSetting.itemSelectedBackgroundColor{
|
||||||
|
didSet{
|
||||||
|
self.itemViews.forEach { (itemView) in
|
||||||
|
itemView.selectedBackgroundColor = itemSelectedBackgroundColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open var sliderViewColor = SegmentControlSetting.selectedViewBackgroundColor{
|
||||||
|
didSet{
|
||||||
|
self.selectedBackgroundView.color = sliderViewColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
open var font = SegmentControlSetting.textFont{
|
||||||
|
didSet{
|
||||||
|
self.itemViews.forEach { (itemView) in
|
||||||
|
itemView.font = font
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open var selectedFont = SegmentControlSetting.selectedTextFont{
|
||||||
|
didSet{
|
||||||
|
self.itemViews.forEach { (itemView) in
|
||||||
|
itemView.selectedFont = selectedFont
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open var items : [String]? {
|
||||||
|
didSet{
|
||||||
|
guard items != nil && items!.count > 0 else {
|
||||||
|
fatalError("Items cannot be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
self.removeAllItemView()
|
||||||
|
|
||||||
|
|
||||||
|
for title in items! {
|
||||||
|
let view = self.createItemView(title: title)
|
||||||
|
self.itemViews.append(view)
|
||||||
|
self.contentView.addSubview(view)
|
||||||
|
}
|
||||||
|
self.selectedIndex = 0
|
||||||
|
|
||||||
|
self.contentView.sendSubviewToBack(self.selectedBackgroundView)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open func showBridge(show:Bool, index:Int){
|
||||||
|
|
||||||
|
guard index < itemViews.count && index >= 0 else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
itemViews[index].showBridge(show: show)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
open var autoScrollWhenIndexChange = false
|
||||||
|
|
||||||
|
open var isShowSeparator = false
|
||||||
|
|
||||||
|
open var scrollToPointWhenIndexChanged : CGPoint?
|
||||||
|
|
||||||
|
open var bounces = false {
|
||||||
|
didSet{
|
||||||
|
self.scrollView.bounces = bounces
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate func removeAllItemView() {
|
||||||
|
itemViews.forEach { (label) in
|
||||||
|
label.removeFromSuperview()
|
||||||
|
}
|
||||||
|
itemViews.removeAll()
|
||||||
|
}
|
||||||
|
private var itemWidths = [CGFloat]()
|
||||||
|
private func createItemView(title:String) -> SegmentItemView {
|
||||||
|
return createItemView(title: title,
|
||||||
|
font: self.font,
|
||||||
|
selectedFont: self.selectedFont,
|
||||||
|
textColor: self.itemTextColor,
|
||||||
|
selectedTextColor: self.itemSelectedTextColor,
|
||||||
|
backgroundColor: UIColor.clear,
|
||||||
|
selectedBackgroundColor: self.itemSelectedBackgroundColor
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func createItemView(title:String, font:UIFont, selectedFont:UIFont, textColor:UIColor, selectedTextColor:UIColor, backgroundColor:UIColor, selectedBackgroundColor:UIColor) -> SegmentItemView {
|
||||||
|
let item = SegmentItemView()
|
||||||
|
|
||||||
|
item.text = title
|
||||||
|
item.textColor = textColor
|
||||||
|
item.textAlignment = .center
|
||||||
|
item.font = font
|
||||||
|
item.selectedFont = selectedFont
|
||||||
|
|
||||||
|
item.itemBackgroundColor = backgroundColor
|
||||||
|
item.selectedTextColor = selectedTextColor
|
||||||
|
item.selectedBackgroundColor = selectedBackgroundColor
|
||||||
|
|
||||||
|
item.state = .Normal
|
||||||
|
return item
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate lazy var scrollView : UIScrollView = {
|
||||||
|
let scrollView = UIScrollView()
|
||||||
|
scrollView.alwaysBounceHorizontal = true
|
||||||
|
scrollView.alwaysBounceVertical = false
|
||||||
|
scrollView.showsHorizontalScrollIndicator = false
|
||||||
|
scrollView.showsVerticalScrollIndicator = false
|
||||||
|
scrollView.bounces = false
|
||||||
|
scrollView.backgroundColor = UIColor.clear
|
||||||
|
return scrollView
|
||||||
|
}()
|
||||||
|
fileprivate lazy var contentView = UIView()
|
||||||
|
|
||||||
|
fileprivate lazy var selectedBackgroundView: SelectedBackgroundView = {
|
||||||
|
let selectedBackgroundView = SelectedBackgroundView.init()
|
||||||
|
|
||||||
|
return selectedBackgroundView
|
||||||
|
}()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
fileprivate var itemViews = [SegmentItemView]()
|
||||||
|
|
||||||
|
fileprivate var numberOfSegments : Int {
|
||||||
|
return itemViews.count
|
||||||
|
}
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
super.init(frame: frame)
|
||||||
|
|
||||||
|
setupViews()
|
||||||
|
|
||||||
|
scrollToPointWhenIndexChanged = scrollView.center
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate func setupViews() {
|
||||||
|
|
||||||
|
addSubview(scrollView)
|
||||||
|
scrollView.addSubview(contentView)
|
||||||
|
contentView.addSubview(selectedBackgroundView)
|
||||||
|
|
||||||
|
|
||||||
|
scrollView.frame = bounds
|
||||||
|
contentView.frame = scrollView.bounds
|
||||||
|
|
||||||
|
scrollView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||||
|
|
||||||
|
addTapGesture()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func addTapGesture() {
|
||||||
|
let tap = UITapGestureRecognizer(target: self, action: #selector(didTapSegement(tapGesture:)))
|
||||||
|
|
||||||
|
contentView.addGestureRecognizer(tap)
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private func didTapSegement(tapGesture:UITapGestureRecognizer) {
|
||||||
|
let index = selectedTargetIndex(gesture: tapGesture)
|
||||||
|
move(to: index)
|
||||||
|
}
|
||||||
|
|
||||||
|
open func move(to index:Int){
|
||||||
|
move(to: index, animated: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
open func move(to index:Int, animated:Bool) {
|
||||||
|
|
||||||
|
let position = centerX(with: index) - 10
|
||||||
|
if animated {
|
||||||
|
UIView.animate(withDuration: 0.2, animations: {
|
||||||
|
self.selectedBackgroundView.center.x = position
|
||||||
|
}) { (finished) in
|
||||||
|
self.delegate?.didSelected?(segement: self, index: index)
|
||||||
|
self.selectedIndex = index
|
||||||
|
|
||||||
|
if self.autoScrollWhenIndexChange {
|
||||||
|
self.scrollItemToPoint(index: index, point: self.scrollToPointWhenIndexChanged!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// 中心点
|
||||||
|
self.selectedBackgroundView.center.x = position
|
||||||
|
self.selectedBackgroundView.center.y = self.center.y
|
||||||
|
|
||||||
|
delegate?.didSelected?(segement: self, index: index)
|
||||||
|
selectedIndex = index
|
||||||
|
|
||||||
|
if autoScrollWhenIndexChange {
|
||||||
|
scrollItemToPoint(index: index, point: scrollToPointWhenIndexChanged!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate func currentItemX(index:Int) -> CGFloat {
|
||||||
|
if autoAdjustWidth {
|
||||||
|
var x:CGFloat = 0.0
|
||||||
|
for i in 0..<index {
|
||||||
|
x += segmentWidth(index: i)
|
||||||
|
}
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
return segementWidth() * CGFloat(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate func centerX(with index:Int) -> CGFloat {
|
||||||
|
if autoAdjustWidth {
|
||||||
|
return currentItemX(index: index) + segmentWidth(index: index)*0.5
|
||||||
|
}
|
||||||
|
return (CGFloat(index) + 0.5)*segementWidth()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func selectedTargetIndex(gesture: UIGestureRecognizer) -> Int {
|
||||||
|
let location = gesture.location(in: contentView)
|
||||||
|
var index = 0
|
||||||
|
|
||||||
|
if autoAdjustWidth {
|
||||||
|
for (i,itemView) in itemViews.enumerated() {
|
||||||
|
if itemView.frame.contains(location) {
|
||||||
|
index = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
index = Int(location.x / segmentWidth(index: selectedIndex))
|
||||||
|
}
|
||||||
|
|
||||||
|
if index < 0 {
|
||||||
|
index = 0
|
||||||
|
}
|
||||||
|
if index > numberOfSegments - 1 {
|
||||||
|
index = numberOfSegments - 1
|
||||||
|
}
|
||||||
|
return index
|
||||||
|
}
|
||||||
|
|
||||||
|
private func scrollItemToCenter(index : Int) {
|
||||||
|
scrollItemToPoint(index: index, point: CGPoint(x: scrollView.bounds.size.width * 0.5, y: 0))
|
||||||
|
}
|
||||||
|
|
||||||
|
private func scrollItemToPoint(index : Int,point:CGPoint) {
|
||||||
|
|
||||||
|
let currentX = currentItemX(index: index)
|
||||||
|
|
||||||
|
let scrollViewWidth = scrollView.bounds.size.width
|
||||||
|
|
||||||
|
var scrollX = currentX - point.x + segmentWidth(index: index) * 0.5
|
||||||
|
|
||||||
|
let maxScrollX = scrollView.contentSize.width - scrollViewWidth
|
||||||
|
|
||||||
|
if scrollX > maxScrollX {
|
||||||
|
scrollX = maxScrollX
|
||||||
|
}
|
||||||
|
if scrollX < 0.0 {
|
||||||
|
scrollX = 0.0
|
||||||
|
}
|
||||||
|
|
||||||
|
scrollView.setContentOffset(CGPoint(x: scrollX, y: 0.0), animated: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
required public init?(coder aDecoder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
open override func layoutSubviews() {
|
||||||
|
super.layoutSubviews()
|
||||||
|
|
||||||
|
guard itemViews.count > 0 else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var x:CGFloat = 0.0
|
||||||
|
let y:CGFloat = 0.0
|
||||||
|
var width:CGFloat = segmentWidth(index: selectedIndex)
|
||||||
|
let height:CGFloat = bounds.size.height
|
||||||
|
|
||||||
|
var contentWidth:CGFloat = 5.0
|
||||||
|
|
||||||
|
|
||||||
|
selectedBackgroundView.frame = CGRect(x: currentItemX(index: selectedIndex) + SegmentControlSetting.selectedViewpadding, y: height - 22, width: 36, height: selectedViewHeight())
|
||||||
|
|
||||||
|
|
||||||
|
self.selectedBackgroundView.center.x = centerX(with: 0) - 10
|
||||||
|
|
||||||
|
|
||||||
|
for (index,item) in itemViews.enumerated() {
|
||||||
|
x = contentWidth
|
||||||
|
width = segmentWidth(index: index)
|
||||||
|
item.frame = CGRect(x: x, y: y, width: width, height: height)
|
||||||
|
|
||||||
|
contentWidth += width
|
||||||
|
}
|
||||||
|
contentView.frame = CGRect(x: 0.0, y: 0.0, width: contentWidth, height: height)
|
||||||
|
scrollView.contentSize = contentView.bounds.size
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
//
|
||||||
|
// Player.swift
|
||||||
|
// IndieMusic
|
||||||
|
//
|
||||||
|
// Created by WenLei on 2023/11/15.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import RxDataSources
|
||||||
|
|
||||||
|
struct PlayerLyrics: Codable {
|
||||||
|
let lyrics: String?
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
struct PlayerLyricsSection {
|
||||||
|
var items: [PlayerLyrics]
|
||||||
|
}
|
||||||
|
|
||||||
|
extension PlayerLyricsSection: SectionModelType {
|
||||||
|
typealias Item = PlayerLyrics
|
||||||
|
|
||||||
|
init(original: PlayerLyricsSection, items: [Item]) {
|
||||||
|
self = original
|
||||||
|
self.items = items
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
//
|
||||||
|
// PlayerViewModel.swift
|
||||||
|
// IndieMusic
|
||||||
|
//
|
||||||
|
// Created by WenLei on 2023/11/15.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import RxSwift
|
||||||
|
import RxCocoa
|
||||||
|
|
||||||
|
class PlayerViewModel: ViewModel, ViewModelType {
|
||||||
|
|
||||||
|
struct Input {
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Output {
|
||||||
|
let items: BehaviorRelay<[PlayerLyricsSection]>
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
let items = BehaviorRelay<[PlayerLyricsSection]>.init(value: [])
|
||||||
|
|
||||||
|
func transform(input: Input) -> Output {
|
||||||
|
|
||||||
|
let lyrics = PlayerLyrics.init(lyrics: "1233211232")
|
||||||
|
let section = PlayerLyricsSection.init(items: [lyrics, lyrics, lyrics, lyrics, lyrics, lyrics, lyrics])
|
||||||
|
|
||||||
|
items.accept([section])
|
||||||
|
|
||||||
|
return Output.init(items: items)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in new issue