今天要说一下需求,我们需要自定义个像日期一样的PickerView,但是在SwiftUI上好像只有一个单列的,没有多列的控件,不过也可以借助UIPickerView来实现,另外我们还可以通过组合来实现这种控件。我们通过一个记录体重的Demo来实现。
首先我们来创建一个工程WeighApp,然后我们在ContentView.swift这个文件里添加一个体重的列表。如下:
var body: some View {
NavigationView {
List {
ForEach(model.dataList) { (item: DataJson) in
Button(action: {
self.currentJson = item;
self.writeDown.toggle()
}) {
DataItemView(json: item)
}
}.onDelete { (set) in
self.currentSet = set
if self.getCurrentJson() != nil {
self.isAlert.toggle()
}
}.alert(isPresented: self.$isAlert) { () -> Alert in
let item = getCurrentJson()!
return Alert(title: Text("提示"), message: Text("是否删除【\(item.date)】记录"), primaryButton: .cancel(), secondaryButton: .default(Text("确定"), action: {
self.model.dataList.removeAll { (remveItem) -> Bool in
remveItem.id == item.id
}
}))
}
}.navigationBarTitle(Text("体重记录"), displayMode: .inline).navigationBarItems(leading: Button(action: {
self.showSeting.toggle()
}, label: {
Text("设置")
}), trailing: Button(action: {
self.writeDown.toggle()
}, label: {
Text("记录")
})).sheet(isPresented: $showSeting) {
SettingView {
self.showSeting.toggle()
}
}
}.sheet(isPresented: $writeDown) {
DatePickerView {
(value: String) in
self.writeDown.toggle()
self.addDateItemToList(value: value);
}
}
}
在这个列表里我们看到一个DatePickerView视图,这个就是我们自定义的一个Picker控件,使用的是SwiftUI控件实现的。来看看下面的代码:
struct DatePickerView: View {
@State var intIndex = UserConfig.weightIndex
@State var decimalIndex = 3
@State var finishedBlock: ((_ value: String)->Void)?
var body: some View {
NavigationView {
VStack(alignment: .center) {
Text("当前体重:\(intIndex).\(decimalIndex)kg").padding().font(Font.system(size: 20)).foregroundColor(Color.rgbColor(r: 255, g: 147, b: 89))
pickerItemsView
Spacer()
}.navigationBarTitle(Text("选择体重"), displayMode: .inline)
.navigationBarItems(trailing: Button(action: {
UserConfig.weightIndex = self.intIndex
self.finishedBlock?(self.intIndex.description + "." + self.decimalIndex.description)
}, label: {
Text("确定")
}))
}
}
var pickerItemsView: some View {
GeometryReader { (geo: GeometryProxy) in
HStack(alignment: .center, spacing: 0.0) {
Picker("kg", selection: self.$intIndex) {
ForEach(0..<80) { (idx: Int) in
Text(idx.description).font(Font.system(size: 20))
}
}.pickerStyle(WheelPickerStyle()).frame(width: geo.size.width/2, height: geo.size.height/2 , alignment: .center).clipped().padding(0)
Picker("", selection: self.$decimalIndex) {
ForEach(0..<10) { (idx: Int) in
Text(idx.description).font(Font.system(size: 20))
}
}.pickerStyle(WheelPickerStyle()).frame(width: geo.size.width/2, height: geo.size.height/2, alignment: .center).clipped()
Spacer()
}.offset(x: 4, y: -90)
}
}
}
在这里需要说明一下,由于目前Picker只支持单列的滑动,所以我们可以利用这个特性也可以创建多个单列的滚动视图。这样看起来就是多列的一个视图了。还有 一个是需要我们去设置通知的,也就是本地的重复提醒:
struct SettingView: View {
@State var isOpen = UserConfig.isOpenNotifiy
@State var date = Date()
@State var dayIndex = UserConfig.dayIndex - 1
@State var finishedBlock: (() -> Void)?
var body: some View {
NavigationView {
List {
Toggle(isOn: $isOpen) {
Text("打开通知")
}
if isOpen {
dateOfDay
datePicker
}
}.navigationBarTitle(Text("通知设置"), displayMode: .inline).navigationBarItems(trailing: Button(action: {
self.finishedBlock?()
}, label: {
Text("确定")
}))
}.onDisappear {
UserConfig.isOpenNotifiy = self.isOpen
if self.isOpen {
UserConfig.dayIndex = self.dayIndex + 1;
NotificationManger.addNotification()
}else {
NotificationManger.removeNotifiation()
}
}
}
var dateOfDay: some View {
HStack {
Text("重复提醒")
Spacer()
Text("每月\(dayIndex + 1)日20:00")
}
}
var datePicker: some View {
HStack {
Picker("picker", selection: $dayIndex) {
ForEach(1..<30) { (index: Int) in
Text(index.description)
}
}.labelsHidden()
}
}
}
这个是一个设置提醒的具体时间的,那么我们接下来看看,在本地通知下提醒的代码:
struct NotificationManger {
static func addNotification() {
let content = UNMutableNotificationContent()
content.badge = 1;
content.title = "今天改称体重了哦"
content.subtitle = ""
if let first = UserModel.init().dataList.first {
content.body = "上次称重时间:\(first.date)\n\(first.kgValue)";
}else {
content.body = "每次记录都会直接保存的哦";
}
content.sound = UNNotificationSound.default;
var resultComponts = DateComponents();
resultComponts.calendar = .init(identifier: .gregorian)
resultComponts.hour = 20;
resultComponts.minute = 0;
resultComponts.second = 0;
resultComponts.day = UserConfig.dayIndex;
let trigger = UNCalendarNotificationTrigger(dateMatching: resultComponts, repeats: true);
let request = UNNotificationRequest.init(identifier: "id", content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request) { (error) in
}
let open = UNNotificationAction(identifier: "open", title: "打开", options: .destructive);
let catorgy = UNNotificationCategory(identifier: "openBack", actions: [open], intentIdentifiers: [], options: .customDismissAction);
UNUserNotificationCenter.current().setNotificationCategories(Set([catorgy]))
}
static func removeNotifiation() {
UNUserNotificationCenter.current().removeAllPendingNotificationRequests();
UNUserNotificationCenter.current().removeAllDeliveredNotifications();
}
}
重复提醒使用UNCalendarNotificationTrigger,是按照日历的设置提醒的,每天提醒只需要设置时间即可,如果每周提醒需要设置时间和第几周提醒,每月提醒需要设置第几日和时间。
结语:对于这个练习可以说是一个很好地练习,我们可以多多做些简单的项目熟悉SwiftUI的数据绑定开发模式,我们以后一定可以事半功倍的。最后献上Demo猛戳这里链接