lgqfzz 2024-08-04 15:00 采纳率: 0%
浏览 6
已结题

Swiftui UIkit日期选择器的问题

SwiftUI和UIkit方面的问题
我是初学者,简单的用法问题,我已精简了程序,这一段程序放入就可运行。程序是一个年月日时分选择器,但是在选择月份的时候,日期始终是31天,没有改变。
我也做了调试,发现返回行数那个函数三次后读取的数值就变成初始值了,也就是8月,8月就是31天,这是为什么?为什么值会变(return parent.myDate.GetDays(yy: parent.myDate.year, mm: parent.myDate.month)这里面的month变了)。我试着在里面读取所选行数selectedRow,代码我注释了,这样能得到正确的结果,但这不是好的处理方式。因为这样处理也有问题,就是阴阳历转换的时候,你没办法从所选的索引读出是阳历还是阴历,最终还是要把参数传进来。
求告知,多谢。

import SwiftUI
import UIKit

// `SimplePicker` 是一个 SwiftUI 视图,它使用 `UIViewRepresentable` 协议将 `UIPickerView` 包装成 SwiftUI 视图。
struct SimplePicker2: UIViewRepresentable {
    @Binding var myDate: MyDateStruct2
    let minYear: Int = 1900
    let maxYear: Int = 2100

    // 创建并返回 `UIPickerView` 实例
    func makeUIView(context: Context) -> UIPickerView {
        let pickerView = UIPickerView()
        pickerView.delegate = context.coordinator // 设置代理
        pickerView.dataSource = context.coordinator // 设置数据源
        pickerView.selectRow(myDate.year - myDate.minYear, inComponent: 0, animated: false)
        pickerView.selectRow(myDate.month - 1, inComponent: 1, animated: false)
        pickerView.selectRow(myDate.day - 1, inComponent: 2, animated: false)
        pickerView.selectRow(myDate.hour - 1, inComponent: 3, animated: false)
        pickerView.selectRow(myDate.minute - 1, inComponent: 4, animated: false)
        return pickerView
    }

    // 当 SwiftUI 需要更新视图时调用,这里不需要更新视图,所以为空实现
    func updateUIView(_ uiView: UIPickerView, context: Context) {
        uiView.reloadAllComponents()
        uiView.selectRow(myDate.year - myDate.minYear, inComponent: 0, animated: false)
        uiView.selectRow(myDate.month - 1, inComponent: 1, animated: false)
        uiView.selectRow(myDate.day - 1, inComponent: 2, animated: false)
        uiView.selectRow(myDate.hour - 1, inComponent: 3, animated: false)
        uiView.selectRow(myDate.minute - 1, inComponent: 4, animated: false)
    }

    // 创建并返回 `Coordinator` 实例
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    // `Coordinator` 类负责处理 `UIPickerView` 的委托和数据源方法
    class Coordinator: NSObject, UIPickerViewDelegate, UIPickerViewDataSource {
        var parent: SimplePicker2
        // 初始化 `Coordinator` 实例,保存对 `SimplePicker` 的引用
        init(_ pickerView: SimplePicker2) {
            self.parent = pickerView
        }

        // 返回选择器的列数 年月日时分 所有五列
        func numberOfComponents(in pickerView: UIPickerView) -> Int {
            return 5 // parent.data.count
        }

        // 返回某列的行数
        func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
            switch component {
            case 0: // 年
                return parent.maxYear - parent.minYear + 1
            case 1: // 月
                return 12
            case 2: // 日
                //let month = pickerView.selectedRow(inComponent: 1) + 1
                return parent.myDate.GetDays(yy: parent.myDate.year, mm: parent.myDate.month)
            case 3: // 时
                return 24
            case 4: // 分
                return 60
            default: // 秒或者错误 其实没有秒的
                return 60
            }
        }

        // 返回某列某行的标题
        func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
            switch component {
            case 0: // 年
                return "\(row + parent.minYear)"
            case 1: // 月
                return "\(row + 1)月"
            case 2: // 日
                return "\(row + 1)日"
            case 3: // 时
                return "\(row)"
            case 4: // 分
                return "\(row)"
            default: // 秒或者错误 其实没有秒的
                return "\(row)"
            }
        }

        // 处理选择事件,打印选择的值
        func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
            switch component {
            case 0:
                parent.myDate.year = row + parent.myDate.minYear
            case 1:
                parent.myDate.month = row + 1
                let newDay = parent.myDate.GetDays(yy: parent.myDate.year, mm: parent.myDate.month)
                if parent.myDate.day > newDay {
                    parent.myDate.day = newDay
                }
                pickerView.reloadComponent(2)
                print("月份改变:\(row + 1)-\(parent.myDate.month)")
            case 2:
                parent.myDate.day = row + 1
            case 3:
                parent.myDate.hour = row
            case 4:
                parent.myDate.minute = row
            default:
                break
            }
        }

        // 设置宽度
        public func pickerView(_ pickerView: UIPickerView, widthForComponent component: Int) -> CGFloat {
            let totalWidth = pickerView.bounds.width
            switch component {
            case 0:
                return totalWidth * 0.24 // 第一列宽度为100
            case 1:
                return totalWidth * 0.21 // 第二列宽度为150
            case 2:
                return totalWidth * 0.23 // 第三列宽度为200
            case 3:
                return totalWidth * 0.15
            default:
                return totalWidth * 0.15
            }
        }
    }
}

struct MyDateStruct2 {
    var minYear = 1900
    var maxYear = 2100
    var year: Int = 2000 // 阳历
    var month: Int = 1
    var day: Int = 1
    var hour: Int = 0
    var minute: Int = 0
    func GetDays(yy: Int, mm: Int) -> Int {
        if mm < 1 || mm > 12 {
            return 0
        }

        if yy == 1582 && mm == 10 {
            return 21
        }

        // 计算是否是闰年
        let isLeapYear = (yy % 4 == 0 && yy % 100 != 0) || (yy % 400 == 0)
        // 每月的天数
        let daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
        // 计算该月的天数
        var dom = daysInMonth[mm - 1]
        if mm == 2 && isLeapYear {
            dom = 29
        }

        return dom
    }
}

// `TestView` 是一个包含 `SimplePicker` 的 SwiftUI 视图
struct TestView2: View {
    @State private var myDate: MyDateStruct2
    init() {
        let calendar = Calendar.current
        let now = Date()
        let components = calendar.dateComponents([.year, .month, .day, .hour, .minute], from: now)
        _myDate = State(initialValue: MyDateStruct2(
            year: components.year!,
            month: components.month!,
            day: components.day!,
            hour: components.hour!,
            minute: components.minute!))
    }

    var body: some View {
        VStack {
            Text("年月日时分1")
            Text("\(String(myDate.year))\(myDate.month)\(myDate.day)\(myDate.hour)\(myDate.minute)分")
            Button(action: {
                myDate.year -= 1
            }, label: {
                Text("阴阳历")
            })
            SimplePicker2(myDate: $myDate)
                .frame(height: 200) // 设置选择器的高度
        }
    }
}

// 预览 `TestView`
#Preview {
    TestView2()
    // SimplePicker()
}


  • 写回答

3条回答 默认 最新

  • 专家-司宇[在线] 2024-08-04 15:00
    关注

    引自免费微信小程序:皆我百晓生

    根据您提供的代码和问题描述,我理解您在尝试创建一个日期选择器,其中可以选择年、月、日、时和分钟。您在月份选择器中遇到的问题是,在选择月份时,日期始终设置为该月的最后一天。我理解您想根据所选的月份动态更新日期。

    问题在于pickerView(_:didSelectRow:inComponent:)方法中,当月份改变时,您重新加载了第3个组件(日),但并没有更新myDate结构体的day属性。因此,即使月份改变了,日期仍然显示为所选月份的最后一天。

    为了解决这个问题,您需要在月份改变后更新日期属性,并相应地调整日期的值。以下是一种可能的解决方案:

    pickerView(_:didSelectRow:inComponent:)方法中,当组件为1(月份)时,除了更新月份外,还需要计算和设置正确的日期值。您可以使用GetDays方法来获取所选月份的天数,然后设置正确的日期值。

    修改后的代码如下所示:

    func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
        switch component {
        case 0: // 年
            parent.myDate.year = row + parent.myDate.minYear
        case 1: // 月
            parent.myDate.month = row + 1
            let newDay = parent.myDate.GetDays(yy: parent.myDate.year, mm: parent.myDate.month)
            parent.myDate.day = min(parent.myDate.day, newDay) // 确保日期不超过该月的最后一天
            pickerView.reloadComponent(2) // 重新加载日组件以更新显示的日期
            print("月份改变:\(row + 1)-\(parent.myDate.month)")
        case 2: // 日
            parent.myDate.day = row + 1
        case 3: // 时
            parent.myDate.hour = row
        case 4: // 分
            parent.myDate.minute = row
        default:
            break
        }
    }
    

    请注意,我还添加了一个对日期的限制,确保它不会超过所选月份的天数。我使用了min函数来比较当前日期和新计算日期的值,以确保不会超过最大天数。这样可以确保在选择月份时不会显示无效日期。

    评论 编辑记录

报告相同问题?

问题事件

  • 已结题 (查看结题原因) 8月4日
  • 创建了问题 8月4日

悬赏问题

  • ¥15 35114 SVAC视频验签的问题
  • ¥15 impedancepy
  • ¥15 在虚拟机环境下完成以下,要求截图!
  • ¥15 求往届大挑得奖作品(ppt…)
  • ¥15 如何在vue.config.js中读取到public文件夹下window.APP_CONFIG.API_BASE_URL的值
  • ¥50 浦育平台scratch图形化编程
  • ¥20 求这个的原理图 只要原理图
  • ¥15 vue2项目中,如何配置环境,可以在打完包之后修改请求的服务器地址
  • ¥20 微信的店铺小程序如何修改背景图
  • ¥15 UE5.1局部变量对蓝图不可见