2025/12/16 新改版后隐藏pickerV选中背景

      // viewDidLayoutSubviews 中调用, 因为这个view在布局完成后才会被加上去,在创建的时候调用没效果

///移除pickerV的灰色圆角背景 [startHourPicker, startMinutePicker, endHourPicker, endMinutePicker] .forEach { removeSystemSelectionHighlight(from: $0) } private func removeSystemSelectionHighlight(from picker: UIPickerView) { picker.subviews.forEach { subview in // 判断条件:高度小(不是内容层) + 有背景色 if subview.frame.height < 100 && (subview.backgroundColor?.alpha ?? 0) > 0 { subview.backgroundColor = .clear } } }

 

 

        //隐藏中间两条分割线
        if pickerV.subviews.count >= 3{
            pickerV.subviews[1].isHidden = true
            pickerV.subviews[2].isHidden = true
        }
        
        //修改选中行的背景色
        for subView in pickerV.subviews {
            if subView.subviews.count != 0 {
                let contentViews = subView.subviews[0]
                for rowView in contentViews.subviews {
                    if rowView.center.y == contentViews.center.y {
                        //背景view
                        rowView.backgroundColor = UIColor.init(hexString: "#F5F5F5")
                        break
                    }
                }
                break
            }
        }

 

 

 // 推荐 显示需要 label 。文字大小, 颜色等
    func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView {
        
        //显示的文字
        var showStr = ""
        switch component {
        case 0:
            showStr = "\(self.dataArr[component][row].selectTextStr)年"
        case 1:
            showStr = "\(self.dataArr[component][row].selectTextStr)月"
        case 2:
            showStr = "\(self.dataArr[component][row].selectTextStr)日"
        default:break
        }
        //修改字体大小, 颜色
        let arrStr = NSAttributedString.highLightText(showStr, highLightString: "", normalFont: UIFont.systemFont(ofSize: 16, weight: UIFont.Weight.regular), highLightFont: nil, normalColor: UIColor.init(hexString: "#333333"), highLightColor: nil)
        
        //这里宽度随便给的, 高度也是随便给的 不能比row的高度大,能显示出来就行
        let showLabel = UILabel(frame: CGRect(x: 0, y: 0, width: 100, height: 34))
        showLabel.textAlignment = .center
        //重新加载label的文字内容
        showLabel.attributedText = arrStr
        return showLabel
    }
    
    //    //改变字体颜色,目前字体大小更改不了,不推荐
    //    func pickerView(_ pickerView: UIPickerView, attributedTitleForRow row: Int, forComponent component: Int) -> NSAttributedString? {
    //
    //
    //        var str = ""
    //        switch component {
    //        case 0:
    //            str = "\(self.dataArr[component][row].selectTextStr)年"
    //        case 1:
    //            str = "\(self.dataArr[component][row].selectTextStr)月"
    //        case 2:
    //            str = "\(self.dataArr[component][row].selectTextStr)日"
    //        default:break
    //        }
    //        return NSAttributedString.highLightText(str, highLightString: "", normalFont: UIFont.systemFont(ofSize: 16, weight: UIFont.Weight.regular), highLightFont: nil, normalColor: UIColor.init(hexString: "#333333"), highLightColor: nil)
    //    }
    
    //每行的高度
    func pickerView(_ pickerView: UIPickerView, rowHeightForComponent component: Int) -> CGFloat {
        return 34
    }

 

案例:选择器 数据 07:00—21:00
// MARK: - 电话告警时间选择器 数据 07:00—21:00
class AlarmTimeRangePicker: UIViewController {
    
    // 回调:确定时返回 (开始时间, 结束时间)
    var onConfirm: ((_ startTimeStr: String, _ endTimeStr: String) -> ())?
    
    // 当前选中值
    private var startHour = 7
    private var startMinute = 0
    private var endHour = 21
    private var endMinute = 0
    
    private let hours = Array(0...23)
    private let minutes = [0, 10, 20, 30, 40, 50]
    
    private let rowHeight: CGFloat = 50
    // UI
    private lazy var containerView: UIView = {
        let v = UIView()
        v.backgroundColor = .white
        v.layer.cornerRadius = 16
        v.clipsToBounds = true
        return v
    }()
    
    private lazy var titleLabel: UILabel = {
        let l = UILabel()
        l.text = "电话告警时间选择"
        l.font = .systemFont(ofSize: 18, weight: .medium)
        l.textAlignment = .center
        l.textColor = .black
        return l
    }()
    
    private lazy var startHourPicker = createPicker(tag: 0)
    private lazy var startMinutePicker = createPicker(tag: 1)
    private lazy var endHourPicker = createPicker(tag: 2)
    private lazy var endMinutePicker = createPicker(tag: 3)
    
    private lazy var toLabel: UILabel = {
        let l = UILabel()
        l.text = ""
        l.font = .yd.emTitleRegular
        l.textColor = R.color.color_242942_FFFFFF()
        return l
    }()
    
    private lazy var selectionOverlay: UIView = {
        let v = UIView()
        v.backgroundColor = UIColor(white: 0.95, alpha: 1)
        v.layer.cornerRadius = 12
        v.isUserInteractionEnabled = false
        return v
    }()
    
    init(defaultStart: String = "07:00",
         defaultEnd: String = "21:00",
         onConfirm: ((String, String) -> Void)? = nil) {
        self.onConfirm = onConfirm
        super.init(nibName: nil, bundle: nil)
        
        modalPresentationStyle = .overFullScreen
        modalTransitionStyle = .crossDissolve
        
        parseDefaultTime(defaultStart: defaultStart, defaultEnd: defaultEnd)
    }
    
    required init?(coder: NSCoder) { fatalError() }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = UIColor.black.withAlphaComponent(0.4)
        
        setupUI()
        
        startHourPicker.selectRow(startHour, inComponent: 0, animated: false)
        startMinutePicker.selectRow(minutes.firstIndex(of: startMinute) ?? 0, inComponent: 0, animated: false)
        endHourPicker.selectRow(endHour, inComponent: 0, animated: false)
        endMinutePicker.selectRow(minutes.firstIndex(of: endMinute) ?? 0, inComponent: 0, animated: false)
    }
    
    ///处理默认时间
    private func parseDefaultTime(defaultStart: String = "07:00",
                                  defaultEnd: String = "21:00") {
        let startParts = defaultStart.split(separator: ":").compactMap { Int($0) }
        let endParts = defaultEnd.split(separator: ":").compactMap { Int($0) }
        
        startHour = startParts.count > 0 ? startParts[0] : 7
        startMinute = startParts.count > 1 ? (startParts[1] / 10 * 10) : 0
        
        endHour = endParts.count > 0 ? endParts[0] : 21
        endMinute = endParts.count > 1 ? (endParts[1] / 10 * 10) : 0
        
        // 保证结束时间不小于开始时间
        if endHour < startHour || (endHour == startHour && endMinute < startMinute) {
            endHour = startHour + 1
            endMinute = 0
        }
    }
    
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        containerView.addCorner(conrners: [.topLeft, .topRight], radius: 12)
        
        ///移除pickerV的灰色圆角背景
        [startHourPicker, startMinutePicker, endHourPicker, endMinutePicker].forEach {
                removeSystemSelectionHighlight(from: $0)
        }
    }
    ///移除pickerV的灰色圆角背景
    private func removeSystemSelectionHighlight(from picker: UIPickerView) {
        picker.subviews.forEach { subview in
            if subview.frame.height < 100 && (subview.backgroundColor?.alpha ?? 0) > 0 {
                subview.backgroundColor = .clear
            }
        }
    }
    

    
    private func createPicker(tag: Int) -> UIPickerView {
        let p = UIPickerView()
        p.tag = tag
        p.delegate = self
        p.dataSource = self
        p.backgroundColor = .clear
        return p
    }
    
    private func setupUI() {
        view.addSubview(containerView)
        containerView.snp.makeConstraints { make in
            make.left.bottom.right.equalToSuperview()
        }
  
        let centerStackV = createCenterPickerStackView()
        let actionBgV = createActionBgView()
        
        [titleLabel, centerStackV, actionBgV]
            .forEach {
                containerView.addSubview($0)
            }
        titleLabel.snp.makeConstraints { make in
            make.top.equalTo(24)
            make.centerX.equalToSuperview()
        }
        centerStackV.snp.makeConstraints { make in
            make.left.right.equalToSuperview().inset(16)
            make.top.equalTo(titleLabel.snp.bottom)
        }
        actionBgV.snp.makeConstraints { make in
            make.left.right.bottom.equalToSuperview()
            make.top.equalTo(centerStackV.snp.bottom)
        }
    }
    
    //中间的 选择器
    private func createCenterPickerStackView() -> UIStackView {
        // 横向排列四个 Picker + “至”
        let pickerRow = UIStackView()
        pickerRow.axis = .horizontal
        pickerRow.spacing = 0
//        pickerRow.spacing = 20
        pickerRow.alignment = .center
//        pickerRow.distribution = .equalSpacing
        pickerRow.distribution = .fill
        
        [startHourPicker, startMinutePicker, toLabel, endHourPicker, endMinutePicker]
            .forEach {
                pickerRow.addArrangedSubview($0)
            }
 
        // 每个 Picker 宽度 + 高度(上下间距更大)
        let pickerWidth: CGFloat = 80
        let pickerHeight: CGFloat = 230  // 上下间距更大
        [startHourPicker, startMinutePicker, endHourPicker, endMinutePicker].forEach { p in
            p.snp.makeConstraints { make in
                make.width.equalTo(pickerWidth)
                make.height.equalTo(pickerHeight)
            }
        }
        
//        toLabel.snp.makeConstraints { make in
//            make.width.equalTo(30)
//        }
        
        //选中的高亮背景
        pickerRow.addSubview(selectionOverlay)
        pickerRow.sendSubviewToBack(selectionOverlay)
        selectionOverlay.snp.makeConstraints { make in
            make.left.right.centerY.equalToSuperview()
            make.height.equalTo(rowHeight)
        }
        return pickerRow
    }
    
    ///底部操作按钮
    private func createActionBgView() -> UIView {
        let bgV = UIView()

        let lineV = UIView()
        lineV.backgroundColor = R.color.text()?.withAlphaComponent(0.05)
        
        let cancelBtn = UIButton(type: .system)
        cancelBtn.setTitle("取消", for: .normal)
        cancelBtn.titleLabel?.font = .yd.buttonRegular
        cancelBtn.setTitleColor(R.color.text_sub(), for: .normal)
        cancelBtn.backgroundColor = .clear
        cancelBtn.layer.cornerRadius = 12
        cancelBtn.layer.borderWidth = 1
        cancelBtn.layer.borderColor = R.color.text_sub()?.cgColor
        cancelBtn.addTarget(self, action: #selector(cancelTapped), for: .touchUpInside)
        
        let confirmBtn = UIButton(type: .system)
        confirmBtn.setTitle("确定", for: .normal)
        confirmBtn.titleLabel?.font = .systemFont(ofSize: 18, weight: .medium)
        confirmBtn.setTitleColor(.white, for: .normal)
        confirmBtn.backgroundColor = .black
        confirmBtn.layer.cornerRadius = 12
        confirmBtn.addTarget(self, action: #selector(confirmTapped), for: .touchUpInside)
        ///添加渐变色
        confirmBtn.yd_setGradientBackground(with: AppTheme.Gradient.black.colors,
                                     locations: [0],
                                     start: CGPoint(x: 0, y: 0),
                                     end: CGPointMake(1, 0))

        [lineV, cancelBtn, confirmBtn]
            .forEach {
                bgV.addSubview($0)
            }
        lineV.snp.makeConstraints { make in
            make.left.top.right.equalToSuperview()
            make.height.equalTo(1)
        }
        cancelBtn.snp.makeConstraints { make in
            make.left.equalTo(16)
            make.top.equalTo(lineV.snp.bottom).offset(16)
            make.height.equalTo(51)
            make.bottom.equalTo(-ydBottomBarHeight()-32)
        }
        confirmBtn.snp.makeConstraints { make in
            make.size.centerY.equalTo(cancelBtn)
            make.left.equalTo(cancelBtn.snp.right).offset(16)
            make.right.equalTo(-16)
        }
        return bgV
    }
    
    @objc private func cancelTapped() {
        dismiss(animated: true)
    }
    
    @objc private func confirmTapped() {
        let start = String(format: "%02d:%02d", startHour, startMinute)
        let end = String(format: "%02d:%02d", endHour, endMinute)
        onConfirm?(start, end)
        dismiss(animated: true)
    }
}

// MARK: - UIPickerView Delegate & DataSource + 选中行蓝色 + 限制逻辑
extension AlarmTimeRangePicker: UIPickerViewDelegate, UIPickerViewDataSource {
    func numberOfComponents(in pickerView: UIPickerView) -> Int {
       return 1
    }
    
    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        switch pickerView.tag {
        case 0, 2:
            return hours.count
        default:
            return minutes.count
        }
    }
    
    func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
        switch pickerView.tag {
        case 0, 2:
            return String(format: "%02d时", hours[row])
        default:
            return String(format: "%02d分", minutes[row])
        }
    }
    
    func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
        switch pickerView.tag {
        case 0: startHour = hours[row]
        case 1: startMinute = minutes[row]
        case 2: endHour = hours[row]
        case 3: endMinute = minutes[row]
        default: break
        }
        
        // 自动限制结束时间不能小于开始时间
        if endHour < startHour || (endHour == startHour && endMinute < startMinute) {
            endHour = startHour
            endMinute = startMinute
            endHourPicker.selectRow(endHour, inComponent: 0, animated: true)
            endMinutePicker.selectRow(minutes.firstIndex(of: endMinute) ?? 0, inComponent: 0, animated: true)
        }
        
        // 刷新所有 Picker
        [startHourPicker, startMinutePicker, endHourPicker, endMinutePicker].forEach { $0.reloadAllComponents() }
    }
    
    func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView {
        var label = view as? UILabel
        if label == nil {
            label = UILabel()
            label?.textAlignment = .center
            label?.font = .systemFont(ofSize: 18, weight: .medium)
            label?.backgroundColor = .clear
        }
        
        var text: String = ""
        switch pickerView.tag {
        case 0, 2:
            text = String(format: "%02d时", hours[row])
        default:
            text = String(format: "%02d分", minutes[row])
        }

        let isSelected = pickerView.selectedRow(inComponent: component) == row
        
        label?.text = text
        label?.font = isSelected ? .yd.emTitleBold : UIFont.systemFont(ofSize: 17, weight: UIFont.Weight.regular)
        label?.textColor = R.color.color_242942_FFFFFF()
        return label!
    }
    
    func pickerView(_ pickerView: UIPickerView, rowHeightForComponent component: Int) -> CGFloat {
        return rowHeight
    }
}

 

 

无限滚动版本

// MARK: - 电话告警时间选择器 数据 07:00—21:00
class AlarmTimeRangePicker: UIViewController {
    
    // 是否开启无限循环滚动(默认开启)
    var isInfiniteLoop: Bool = true
    
    // 循环倍数(仅在开启无限循环时使用)
    private let loopMultiplier = 1000
    
    // 回调:确定时返回 (开始时间, 结束时间)
    var onConfirm: ((_ startTimeStr: String, _ endTimeStr: String) -> ())?
    
    // 当前选中值
    private var startHour = 7
    private var startMinute = 0
    private var endHour = 21
    private var endMinute = 0
    
    private let hours = Array(0...23)
    private let minutes = [0, 10, 20, 30, 40, 50]
    
    private let rowHeight: CGFloat = 50
    // UI
    private lazy var containerView: UIView = {
        let v = UIView()
        v.backgroundColor = .white
        v.layer.cornerRadius = 16
        v.clipsToBounds = true
        return v
    }()
    
    private lazy var titleLabel: UILabel = {
        let l = UILabel()
        l.text = "电话告警时间选择"
        l.font = .systemFont(ofSize: 18, weight: .medium)
        l.textAlignment = .center
        l.textColor = .black
        return l
    }()
    
    private lazy var startHourPicker = createPicker(tag: 0)
    private lazy var startMinutePicker = createPicker(tag: 1)
    private lazy var endHourPicker = createPicker(tag: 2)
    private lazy var endMinutePicker = createPicker(tag: 3)
    
    private lazy var toLabel: UILabel = {
        let l = UILabel()
        l.text = ""
        l.font = .yd.emTitleRegular
        l.textColor = R.color.color_242942_FFFFFF()
        return l
    }()
    
    private lazy var selectionOverlay: UIView = {
        let v = UIView()
        v.backgroundColor = UIColor(white: 0.95, alpha: 1)
        v.layer.cornerRadius = 12
        v.isUserInteractionEnabled = false
        return v
    }()
    
    init(defaultStart: String = "07:00",
         defaultEnd: String = "21:00",
         isInfiniteLoop: Bool = true,
         onConfirm: ((String, String) -> Void)? = nil) {
        self.onConfirm = onConfirm
        self.isInfiniteLoop = isInfiniteLoop
        super.init(nibName: nil, bundle: nil)
        
        modalPresentationStyle = .overFullScreen
        modalTransitionStyle = .crossDissolve
        
        parseDefaultTime(defaultStart: defaultStart, defaultEnd: defaultEnd)
    }
    
    required init?(coder: NSCoder) { fatalError() }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = UIColor.black.withAlphaComponent(0.4)
        
        setupUI()
        
        startHourPicker.selectRow(
            isInfiniteLoop ? displayedRow(from: startHour, isHour: true) : startHour,
            inComponent: 0, animated: false)

        startMinutePicker.selectRow(
            isInfiniteLoop ? displayedRow(from: startMinute, isHour: false) : (minutes.firstIndex(of: startMinute) ?? 0),
            inComponent: 0, animated: false)

        endHourPicker.selectRow(
            isInfiniteLoop ? displayedRow(from: endHour, isHour: true) : endHour,
            inComponent: 0, animated: false)

        endMinutePicker.selectRow(
            isInfiniteLoop ? displayedRow(from: endMinute, isHour: false) : (minutes.firstIndex(of: endMinute) ?? 0),
            inComponent: 0, animated: false)
    }
    
    // 计算显示行(仅无限循环时使用)
    private func displayedRow(from realValue: Int, isHour: Bool) -> Int {
        let array = isHour ? hours : minutes
        let index = array.firstIndex(of: realValue) ?? 0
        return index + array.count * (loopMultiplier / 2)
    }
    
    // 计算真实行
    private func realRow(from displayedRow: Int, totalCount: Int) -> Int {
        return displayedRow % totalCount
    }
    
    ///处理默认时间
    private func parseDefaultTime(defaultStart: String = "07:00",
                                  defaultEnd: String = "21:00") {
        let startParts = defaultStart.split(separator: ":").compactMap { Int($0) }
        let endParts = defaultEnd.split(separator: ":").compactMap { Int($0) }
        
        startHour = startParts.count > 0 ? startParts[0] : 7
        startMinute = startParts.count > 1 ? (startParts[1] / 10 * 10) : 0
        
        endHour = endParts.count > 0 ? endParts[0] : 21
        endMinute = endParts.count > 1 ? (endParts[1] / 10 * 10) : 0
        
        // 保证结束时间不小于开始时间
        if endHour < startHour || (endHour == startHour && endMinute < startMinute) {
            endHour = startHour + 1
            endMinute = 0
        }
    }
    
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        containerView.addCorner(conrners: [.topLeft, .topRight], radius: 12)
        
        ///移除pickerV的灰色圆角背景
        [startHourPicker, startMinutePicker, endHourPicker, endMinutePicker].forEach {
                removeSystemSelectionHighlight(from: $0)
        }
    }
    ///移除pickerV的灰色圆角背景
    private func removeSystemSelectionHighlight(from picker: UIPickerView) {
        picker.subviews.forEach { subview in
            if subview.frame.height < 100 && (subview.backgroundColor?.alpha ?? 0) > 0 {
                subview.backgroundColor = .clear
            }
        }
    }
    
    private func createPicker(tag: Int) -> UIPickerView {
        let p = UIPickerView()
        p.tag = tag
        p.delegate = self
        p.dataSource = self
        p.backgroundColor = .clear
        return p
    }
    
    private func setupUI() {
        view.addSubview(containerView)
        containerView.snp.makeConstraints { make in
            make.left.bottom.right.equalToSuperview()
        }
  
        let centerStackV = createCenterPickerStackView()
        let actionBgV = createActionBgView()
        
        [titleLabel, centerStackV, actionBgV]
            .forEach {
                containerView.addSubview($0)
            }
        titleLabel.snp.makeConstraints { make in
            make.top.equalTo(24)
            make.centerX.equalToSuperview()
        }
        centerStackV.snp.makeConstraints { make in
            make.left.right.equalToSuperview().inset(16)
            make.top.equalTo(titleLabel.snp.bottom)
        }
        actionBgV.snp.makeConstraints { make in
            make.left.right.bottom.equalToSuperview()
            make.top.equalTo(centerStackV.snp.bottom)
        }
    }
    
    //中间的 选择器
    private func createCenterPickerStackView() -> UIStackView {
        // 横向排列四个 Picker + “至”
        let pickerRow = UIStackView()
        pickerRow.axis = .horizontal
        pickerRow.spacing = 0
//        pickerRow.spacing = 20
        pickerRow.alignment = .center
//        pickerRow.distribution = .equalSpacing
        pickerRow.distribution = .fill
        
        [startHourPicker, startMinutePicker, toLabel, endHourPicker, endMinutePicker]
            .forEach {
                pickerRow.addArrangedSubview($0)
            }
 
        // 每个 Picker 宽度 + 高度(上下间距更大)
        let pickerWidth: CGFloat = 80
        let pickerHeight: CGFloat = 230  // 上下间距更大
        [startHourPicker, startMinutePicker, endHourPicker, endMinutePicker].forEach { p in
            p.snp.makeConstraints { make in
                make.width.equalTo(pickerWidth)
                make.height.equalTo(pickerHeight)
            }
        }
        
//        toLabel.snp.makeConstraints { make in
//            make.width.equalTo(30)
//        }
        
        //选中的高亮背景
        pickerRow.addSubview(selectionOverlay)
        pickerRow.sendSubviewToBack(selectionOverlay)
        selectionOverlay.snp.makeConstraints { make in
            make.left.right.centerY.equalToSuperview()
            make.height.equalTo(rowHeight)
        }
        return pickerRow
    }
    
    ///底部操作按钮
    private func createActionBgView() -> UIView {
        let bgV = UIView()

        let lineV = UIView()
        lineV.backgroundColor = R.color.text()?.withAlphaComponent(0.05)
        
        let cancelBtn = UIButton(type: .system)
        cancelBtn.setTitle("取消", for: .normal)
        cancelBtn.titleLabel?.font = .yd.buttonRegular
        cancelBtn.setTitleColor(R.color.text_sub(), for: .normal)
        cancelBtn.backgroundColor = .clear
        cancelBtn.layer.cornerRadius = 12
        cancelBtn.layer.borderWidth = 1
        cancelBtn.layer.borderColor = R.color.text_sub()?.cgColor
        cancelBtn.addTarget(self, action: #selector(cancelTapped), for: .touchUpInside)
        
        let confirmBtn = UIButton(type: .system)
        confirmBtn.setTitle("确定", for: .normal)
        confirmBtn.titleLabel?.font = .systemFont(ofSize: 18, weight: .medium)
        confirmBtn.setTitleColor(.white, for: .normal)
        confirmBtn.backgroundColor = .black
        confirmBtn.layer.cornerRadius = 12
        confirmBtn.addTarget(self, action: #selector(confirmTapped), for: .touchUpInside)
        ///添加渐变色
        confirmBtn.yd_setGradientBackground(with: AppTheme.Gradient.black.colors,
                                     locations: [0],
                                     start: CGPoint(x: 0, y: 0),
                                     end: CGPointMake(1, 0))

        [lineV, cancelBtn, confirmBtn]
            .forEach {
                bgV.addSubview($0)
            }
        lineV.snp.makeConstraints { make in
            make.left.top.right.equalToSuperview()
            make.height.equalTo(1)
        }
        cancelBtn.snp.makeConstraints { make in
            make.left.equalTo(16)
            make.top.equalTo(lineV.snp.bottom).offset(16)
            make.height.equalTo(51)
            make.bottom.equalTo(-ydBottomBarHeight()-32)
        }
        confirmBtn.snp.makeConstraints { make in
            make.size.centerY.equalTo(cancelBtn)
            make.left.equalTo(cancelBtn.snp.right).offset(16)
            make.right.equalTo(-16)
        }
        return bgV
    }
    
    @objc private func cancelTapped() {
        dismiss(animated: true)
    }
    
    @objc private func confirmTapped() {
        let start = String(format: "%02d:%02d", startHour, startMinute)
        let end = String(format: "%02d:%02d", endHour, endMinute)
        onConfirm?(start, end)
        dismiss(animated: true)
    }
}

// MARK: - UIPickerView Delegate & DataSource + 选中行蓝色 + 限制逻辑
extension AlarmTimeRangePicker: UIPickerViewDelegate, UIPickerViewDataSource {
    // 判断是否是小时 Picker
    private func isHourPicker(_ pickerView: UIPickerView) -> Bool {
        return pickerView.tag == 0 || pickerView.tag == 2
    }

    // 判断是否是分钟 Picker
    private func isMinutePicker(_ pickerView: UIPickerView) -> Bool {
        return pickerView.tag == 1 || pickerView.tag == 3
    }
    
    func numberOfComponents(in pickerView: UIPickerView) -> Int {
       return 1
    }
    
    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        let baseCount = isHourPicker(pickerView) ? hours.count : minutes.count
        return isInfiniteLoop ? baseCount * loopMultiplier : baseCount
    }
    
    func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
        let baseCount = isHourPicker(pickerView) ? hours.count : minutes.count
        let realRow = isInfiniteLoop ? realRow(from: row, totalCount: baseCount) : row
        
        let value = isHourPicker(pickerView) ? hours[realRow] : minutes[realRow]
        return isHourPicker(pickerView) ? String(format: "%02d时", value) : String(format: "%02d分", value)
    }
    
    func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
        let baseCount = isHourPicker(pickerView) ? hours.count : minutes.count
        let realRow = isInfiniteLoop ? realRow(from: row, totalCount: baseCount) : row
        let value = isHourPicker(pickerView) ? hours[realRow] : minutes[realRow]
        
        switch pickerView.tag {
        case 0: startHour = value
        case 1: startMinute = value
        case 2: endHour = value
        case 3: endMinute = value
        default: break
        }
        
        // 自动限制结束时间不能小于开始时间
        if endHour < startHour || (endHour == startHour && endMinute < startMinute) {
            endHour = startHour
            endMinute = startMinute
            let endHourRow = isInfiniteLoop ? displayedRow(from: endHour, isHour: true) : endHour
            let endMinuteRow = isInfiniteLoop ? displayedRow(from: endMinute, isHour: false) : minutes.firstIndex(of: endMinute) ?? 0
            endHourPicker.selectRow(endHourRow, inComponent: 0, animated: true)
            endMinutePicker.selectRow(endMinuteRow, inComponent: 0, animated: true)
        }
        
        // 刷新所有 Picker
        [startHourPicker, startMinutePicker, endHourPicker, endMinutePicker].forEach { $0.reloadAllComponents() }
    }
    
    func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView {
        var label = view as? UILabel
        if label == nil {
            label = UILabel()
            label?.textAlignment = .center
            label?.font = .systemFont(ofSize: 18, weight: .medium)
            label?.backgroundColor = .clear
        }
        
        // 1. 计算真实行号(支持无限循环)
        let isHour = (pickerView.tag == 0 || pickerView.tag == 2)
        let baseCount = isHour ? hours.count : minutes.count
        let realRow = isInfiniteLoop ? (row % baseCount) : row
        
        // 2. 获取当前值
        let value = isHour ? hours[realRow] : minutes[realRow]
        
        // 3. 生成显示文字
        let text = isHour ? String(format: "%02d时", value) : String(format: "%02d分", value)
        
        // 4. 判断是否是当前选中行
        let selectedValue: Int
        switch pickerView.tag {
        case 0: selectedValue = startHour
        case 1: selectedValue = startMinute
        case 2: selectedValue = endHour
        case 3: selectedValue = endMinute
        default: selectedValue = 0
        }
        
        let isSelected = value == selectedValue
        
        label?.text = text
        label?.font = isSelected ? .yd.emTitleBold : UIFont.systemFont(ofSize: 17, weight: UIFont.Weight.regular)
        label?.textColor = R.color.color_242942_FFFFFF()
        return label!
    }
    
    func pickerView(_ pickerView: UIPickerView, rowHeightForComponent component: Int) -> CGFloat {
        return rowHeight
    }
}

 

posted on 2019-10-11 18:20  懂事长qingzZ  阅读(2307)  评论(0)    收藏  举报