SwiftUI极简教程21:CoreData数据持久化框架的使用(中)

简介: SwiftUI极简教程21:CoreData数据持久化框架的使用(中)

承接上一章的内容,我们继续完成使用CoreData框架搭建一个简单的ToDo待办事项App


这一章节,我们完成一下NewToDoView新建事项页面。

image.png

我们先新建一个新页面,命名为NewToDoView

点击Xcode顶部导航栏,File文件,New新建,选择File创建文件,选择iOS中的SwiftUI File类型的文件,命名为NewToDoView.swift

image.png

页面UI设计


我们还是从上往下构建UI页面。

TopNavBar顶部导航栏视图

image.png

首先是TopNavBar顶部导航栏,名称不能和之前创建的重复,它由一个Text标题一个closeButton关闭按钮组成。


// 顶部导航栏
struct TopNavBar: View {
    var body: some View {
        HStack {
            Text("新建事项")
                .font(.system(.title))
                .bold()
            Spacer()
            Button(action: {
            }) {
                Image(systemName: "xmark.circle.fill")
                    .foregroundColor(.gray)
                    .font(.title)
            }
        }
    }
}


image.png

InputNameView输入框视图


然后是事项名称的输入框TextField

TextField输入框需要有两个参数绑定,一个是内容绑定,即我们TextField输入框需要记录什么内容。第二个是isEditing输入状态绑定,帮助我们检测它是否正在输入,后面我们会用到输入的状态的检测。

我们在NewToDoView视图中,使用@State声明两个变量。


@State var name: String
@State var isEditing = false


然后我们再构建InputNameView输入框视图的内容,再绑定参数。


//输入框
struct InputNameView: View {
    @Binding var name: String
    @Binding var isEditing: Bool
    var body: some View {
        TextField("请输入", text: $name, onEditingChanged: { (editingChanged) in
            self.isEditing = editingChanged
        })
            .padding()
            .background(Color(.systemGray6))
            .cornerRadius(8)
            .padding(.bottom)
    }
}


最后在NewToDoView视图中展示InputNameView输入框视图的内容,这里用VStack垂直排布将InputNameView输入框视图和TopNavBar顶部导航栏排在一起。


VStack {
    TopNavBar()
    InputNameView(name: $name, isEditing: $isEditing)
}


由于我们NewToDoView视图需要预览,因此要想在模拟器中看到效果,还需要在NewToDoView_Previews预览视图中添加参数。

运行一下,我们看下效果。

image.png

下面我们继续,接下来是事项的优先级选择,我们先完成UI的部分。


PrioritySelectView优先级选择视图


我们命名一个PrioritySelectView优先级选择视图,这里当然也可以用代码整合的方式减少下代码量,我们将相同的修饰符抽离出来,然后再在PrioritySelectView优先级选择视图展示内容。


// 选择优先级
struct PrioritySelectView: View {
    var body: some View {
        HStack {
            PrioritySelectRow(name: "高", color: Color.red)
            PrioritySelectRow(name: "中", color: Color.orange)
            PrioritySelectRow(name: "低", color: Color.green)
        }
    }
}
// 选择优先级
struct PrioritySelectRow: View {
    var name: String
    var color:Color
    var body: some View {
        Text(name)
            .frame(width: 80)
            .font(.system(.headline))
            .padding(10)
            .background(color)
            .foregroundColor(.white)
            .cornerRadius(8)
    }
}


我们把PrioritySelectView加到NewToDoView视图中看下效果。


VStack {
    TopNavBar()
    InputNameView(name: $name, isEditing: $isEditing)
    PrioritySelectView()
}


image.png

SaveButton保存按钮视图


接下来是SaveButton保存按钮的绘制,我们让按钮下面留点底边距。

我们也加进去NewToDoView视图看看效果。


// 保存按钮
struct SaveButton: View {
    var body: some View {
        Button(action: {
        }) {
            Text("保存")
                .font(.system(.headline))
                .frame(minWidth: 0, maxWidth: .infinity)
                .padding()
                .foregroundColor(.white)
                .background(Color.blue)
                .cornerRadius(8)
        }
        .padding([.top,.bottom])
    }
}


image.png

NewToDoView页面位置调整


然后,我们调整下位置,我们希望这个NewToDoView页面是从底部弹出来,然后内容也都在底部展示而不是居中,我们可以调整下整个NewToDoView页面的位置。

我们用VStackSpacerNewToDoView视图顶到底部,然后根据InputNameView输入框是否处于输入状态isEditing,来进行偏移,也就是当我们点击InputNameView输入框正在输入的时候,整个视图可以向上移动,这样我们的keyboard键盘输入就有位置正对应了,这是一个小技巧

然后整个NewToDoView页面页面我们再置底一点,使用.edgesIgnoringSafeArea安全区域空出底部区域,这样好看很多。


VStack {
    Spacer()
    VStack {
        TopNavBar()
        InputNameView(name: $name, isEditing: $isEditing)
        PrioritySelectView()
        SaveButton()
    }
    .padding()
    .background(Color.white)
    .cornerRadius(10, antialiased: true)
    .offset(y: isEditing ? -320 : 0)
}.edgesIgnoringSafeArea(.bottom)

image.png

交互逻辑设计


好了,我们完成了NewToDoView页面的绘制了,下面是逻辑部分。

PrioritySelectView优先级选择逻辑


首先是我们的PrioritySelectView优先级的选择,我们希望点击选择哪个优先级,哪个优先级就“亮起”,这样我们好知道选中的是哪一个。

image.png

同样,我们需要储存priority优先级状态,priority优先级是存储在NewToDoView新增事项页面里的,这里用@State状态。


//NewToDoView视图中定义
@State var priority: Priority

然后,我们完善下PrioritySelectView优先级的选择页面,根据选中状态展示背景颜色,如果没选中,我们就变成.systemGray4灰色。

// 选择优先级
struct PrioritySelectView: View {
    @Binding var priority: Priority
    var body: some View {
        HStack {
            PrioritySelectRow(name: "高", color: priority == .high ? Color.red : Color(.systemGray4))
                .onTapGesture { self.priority = .high }
            PrioritySelectRow(name: "中", color: priority == .normal ? Color.orange : Color(.systemGray4))
                .onTapGesture { self.priority = .normal }
            PrioritySelectRow(name: "低", color: priority == .low ? Color.green : Color(.systemGray4))
                .onTapGesture { self.priority = .low }
        }
    }
}

我们完善下NewToDoView视图的绑定关系,顺便给个示例数据预览下模拟器结果。

struct NewToDoView: View {
    @State var name: String
    @State var isEditing = false
    @State var priority: Priority
    var body: some View {
        VStack {
            Spacer()
            VStack {
                TopNavBar()
                InputNameView(name: $name, isEditing: $isEditing)
                PrioritySelectView(priority: $priority)
                SaveButton()
            }
            .padding()
            .background(Color.white)
            .cornerRadius(10, antialiased: true)
            .offset(y: isEditing ? -320 : 0)
        }.edgesIgnoringSafeArea(.bottom)
    }
}
struct NewToDoView_Previews: PreviewProvider {
    static var previews: some View {
        NewToDoView(name: "", todoItems: .constant([]), priority: .normal)
    }
}

image.png

页面弹出逻辑

让我们回到ContentView首页,我们将两个页面联动起来。

image.png

页面弹出的交互逻辑是,当我们点击ContentView首页右上角的添加按钮时,打开NewToDoView新增事项页面。

明白了逻辑之后,我们现在ContentView首页写逻辑,先声明一个变量showNewTask,表示我们是否打开了NewToDoView新增事项页面,默认是false


@State private var showNewTask = false
@State private var offset: CGFloat = .zero    //使用.animation防止报错,iOS15的特性
复制代码


如果showNewTask状态为true时,我们显示NewToDoView新增事项页面,我们可以把NewToDoView新增事项页面放在ContentView首页的ZStack包裹着。


//点击添加时打开弹窗
if showNewTask {
    NewToDoView(name: "", priority: .normal)
        .transition(.move(edge: .bottom))
        .animation(.interpolatingSpring(stiffness: 200.0, damping: 25.0, initialVelocity: 10.0),value: offset)
    }


然后我们增加点击事件,当我们在ContentView首页点击添加按钮的时候,showNewTask状态变为为true


// 顶部导航栏
struct TopBarMenu: View {
    @Binding var showNewTask: Bool
    var body: some View {
        HStack {
            Text("待办事项")
                .font(.system(size: 40, weight: .black))
            Spacer()
            Button(action: {
                //打开弹窗
                self.showNewTask = true
            }) {
                Image(systemName: "plus.circle.fill")
                    .font(.largeTitle).foregroundColor(.blue)
            }
        }
        .padding()
    }
}


//ContentView视图
TopBarMenu(showNewTask: $showNewTask)


image.png

好像基本完成了效果,但由于我们是使用ZStack包裹的方式,而不是用ModelView模态弹窗或者 NavigationView导航栏进入新的页面,所以我们还需要做一个MaskView蒙层遮住背景,让它看起来像是弹窗的效果。


MaskView蒙层逻辑


//蒙层
struct MaskView : View {
    var bgColor: Color
    var body: some View {
        VStack {
            Spacer()
        }
        .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
        .background(bgColor)
        .edgesIgnoringSafeArea(.all)
    }
}


然后把MaskView蒙层加到打开NewToDoView新增事项页面的逻辑里,同时,支持我们点击MaskView蒙层关闭弹窗。


//蒙层
MaskView(bgColor: .black)
    .opacity(0.5)
    .onTapGesture {
        self.showNewTask = false
    }


image.png

好!我们实现了怎么弹出NewToDoView新增事项页面,我们回到NewToDoView.swift文件,我们实现如何点击关闭弹窗。

页面关闭逻辑


NewToDoView新增事项页面关闭有两种,一种是点击关闭按钮关闭弹窗。


// 顶部导航栏
struct TopNavBar: View {
    @Binding var showNewTask: Bool
    var body: some View {
        HStack {
            Text("新建事项")
                .font(.system(.title))
                .bold()
            Spacer()
            Button(action: {
                //关闭弹窗
                self.showNewTask = false
            }) {
                Image(systemName: "xmark.circle.fill")
                    .foregroundColor(.gray)
                    .font(.title)
            }
        }
    }
}
复制代码
//NewToDoView视图
struct NewToDoView: View {
    @State var name: String
    @State var isEditing = false
    @State var priority: Priority
    @Binding var showNewTask: Bool
    var body: some View {
        VStack {
            Spacer()
            VStack {
                TopNavBar(showNewTask: $showNewTask)
                InputNameView(name: $name, isEditing: $isEditing)
                PrioritySelectView(priority: $priority)
                SaveButton()
            }
            .padding()
            .background(Color.white)
            .cornerRadius(10, antialiased: true)
            .offset(y: isEditing ? -320 : 0)
        }.edgesIgnoringSafeArea(.bottom)
    }
}


image.png

我们发现系统报错了,这是因为我们使用@Binding绑定了是否展示页面showNewTask的布尔值,还需要在ContentView首页建立关联。


//ContentView视图
NewToDoView(name: "", priority: .normal, showNewTask: $showNewTask)


image.png

这样,我们就完成了第一种关闭弹窗的交互:点击关闭按钮关闭弹窗。

另一种关闭弹窗的交互是,我们新建一个事项,满足条件后(内容不为空),这是我们点击saveButton保存按钮,关闭弹窗

我们再回到NewToDoView.swift文件。首先我们保存要校验下InputNameView输入框内容是否为空为空的时候我们不关闭弹窗。当InputNameView输入框内容不为空的时候,我们才允许关闭弹窗


// 保存按钮
struct SaveButton: View {
    @Binding var name:String
    @Binding var showNewTask: Bool
    var body: some View {
        Button(action: {
            //判断输入框是否为空
            if self.name.trimmingCharacters(in: .whitespaces) == "" {
                return
            }
            //关闭弹窗
            self.showNewTask = false
        }) {
            Text("保存")
                .font(.system(.headline))
                .frame(minWidth: 0, maxWidth: .infinity)
                .padding()
                .foregroundColor(.white)
                .background(Color.blue)
                .cornerRadius(8)
        }
        .padding([.top,.bottom])
    }
}


//NewToDoView视图
SaveButton(name: $name, showNewTask: $showNewTask)


我们回到ContentView.swift文件中,运行模拟器体验下。

我们完成了基础的关闭弹窗操作,可以点击关闭按钮关闭,也可以输入新建事项后,点击保存关闭弹窗。

image.png

添加新事项逻辑


我们在NewToDoView添加完事项后,输入的内容和选择的优先级就会在ContentView首页List列表中创建一条数据,下面我们来完成添加新事项逻辑。

image.png

我们看回NewToDoView.swift文件,我们实现了有输入内容时,点击保存按钮关闭弹窗,但没有实现addTask新增数据,下面我们来实现它。


// 保存按钮
struct SaveButton: View {
    @Binding var name:String
    @Binding var showNewTask: Bool
    @Binding var todoItems: [ToDoItem]
    @Binding var priority:Priority
    var body: some View {
        Button(action: {
            //判断输入框是否为空
            if self.name.trimmingCharacters(in: .whitespaces) == "" {
                return
            }
            //添加一条新数据
            self.addTask(name: self.name, priority: self.priority)
            //关闭弹窗
            self.showNewTask = false
        }) {
            Text("保存")
                .font(.system(.headline))
                .frame(minWidth: 0, maxWidth: .infinity)
                .padding()
                .foregroundColor(.white)
                .background(Color.blue)
                .cornerRadius(8)
        }
        .padding([.top,.bottom])
    }
    //添加新事项方法
    private func addTask(name: String, priority: Priority, isCompleted: Bool = false) {
        let task = ToDoItem(name: name, priority: priority, isCompleted: isCompleted)
        todoItems.append(task)
    }
}
复制代码


我们定义一个addTask添加事项的private私有方法,添加的参数是name内容、priority优先级、isCompleted是否完成,默认为否false。然后实例化它,调用方法的时候在 todoItems数组中增加一条数据。然后,我们点击SaveBotton保存成功时调用addTask添加新事项方法。


//NewToDoView视图
struct NewToDoView: View {
    @State var name: String
    @State var isEditing = false
    @State var priority: Priority
    @Binding var showNewTask: Bool
    @Binding var todoItems: [ToDoItem]
    var body: some View {
        VStack {
            Spacer()
            VStack {
                TopNavBar(showNewTask: $showNewTask)
                InputNameView(name: $name, isEditing: $isEditing)
                PrioritySelectView(priority: $priority)
                SaveButton(name: $name, showNewTask: $showNewTask, todoItems: $todoItems, priority: $priority)
            }
            .padding()
            .background(Color.white)
            .cornerRadius(10, antialiased: true)
            .offset(y: isEditing ? -320 : 0)
        }.edgesIgnoringSafeArea(.bottom)
    }
}
struct NewToDoView_Previews: PreviewProvider {
    static var previews: some View {
        NewToDoView(name: "", priority: .normal, showNewTask: .constant(true), todoItems: .constant([]))
    }
}
复制代码

同时,我们在NewToDoView视图绑定关联关系,并在NewToDoView_Previews预览视图中也绑定好关系。

当然别忘了,还要在 ContentView首页视图绑定参数。


// ContentView视图
NewToDoView(name: "", priority: .normal, showNewTask: $showNewTask, todoItems: $todoItems)
复制代码


恭喜你,我们就完成了ContentView首页视图和NewToDoView新建事项视图的全部交互逻辑!


未完待续


但还没有全部完成,我们只是完成了一个简单的ToDo待办事项的App,还没有实现CoreData数据持久化。

由于篇幅过长,上篇我们完成了ContentView首页视图的制作,中篇我们完成NewToDoView新建事项视图的制作,当然还有他们之间的交互

CoreData数据持久化框架的使用将再分出下篇,我们看看如何使用CoreData数据持久化框架,真正实现一个可以保存数据App

本章完整代码如下:


//ToDoItem.swift
import Foundation
enum Priority: Int {
    case low = 0
    case normal = 1
    case high = 2
}
class ToDoItem: ObservableObject, Identifiable {
    var id = UUID()
    @Published var name: String = ""
    @Published var priority: Priority = .high
    @Published var isCompleted: Bool = false
    init(name: String, priority: Priority = .normal, isCompleted: Bool = false) {
        self.name = name
        self.priority = priority
        self.isCompleted = isCompleted
    }
}


//ContentView.swift
import CoreData
import SwiftUI
struct ContentView: View {
    @State var todoItems: [ToDoItem] = []
    @State private var showNewTask = false
    @State private var offset: CGFloat = .zero    //使用.animation防止报错,iOS15的特性
    //去掉List背景颜色
    init() {
        UITableView.appearance().backgroundColor = .clear
        UITableViewCell.appearance().backgroundColor = .clear
    }
    var body: some View {
        ZStack {
            VStack {
                TopBarMenu(showNewTask: $showNewTask)
                ToDoListView(todoItems: $todoItems)
            }
            //判断事项数量为0时展示缺省图
            if todoItems.count == 0 {
                NoDataView()
            }
            //点击添加时打开弹窗
            if showNewTask {
                //蒙层
                MaskView(bgColor: .black)
                    .opacity(0.5)
                    .onTapGesture {
                        self.showNewTask = false
                    }
                NewToDoView(name: "", priority: .normal, showNewTask: $showNewTask, todoItems: $todoItems)
                    .transition(.move(edge: .bottom))
                    .animation(.interpolatingSpring(stiffness: 200.0, damping: 25.0, initialVelocity: 10.0),value: offset)
                }
        }
    }
}
//蒙层
struct MaskView : View {
    var bgColor: Color
    var body: some View {
        VStack {
            Spacer()
        }
        .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
        .background(bgColor)
        .edgesIgnoringSafeArea(.all)
    }
}
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
// 顶部导航栏
struct TopBarMenu: View {
    @Binding var showNewTask: Bool
    var body: some View {
        HStack {
            Text("待办事项")
                .font(.system(size: 40, weight: .black))
            Spacer()
            Button(action: {
                //打开弹窗
                self.showNewTask = true
            }) {
                Image(systemName: "plus.circle.fill")
                    .font(.largeTitle).foregroundColor(.blue)
            }
        }
        .padding()
    }
}
// 缺省图
struct NoDataView: View {
    var body: some View {
        Image("image01")
            .resizable()
            .scaledToFit()
    }
}
// 列表
struct ToDoListView: View {
    @Binding var todoItems: [ToDoItem]
    var body: some View {
        List {
            ForEach(todoItems) { todoItem in
                ToDoListRow(todoItem: todoItem)
            }
        }
    }
}
// 列表内容
struct ToDoListRow: View {
    @ObservedObject var todoItem: ToDoItem
    var body: some View {
        Toggle(isOn: self.$todoItem.isCompleted) {
            HStack {
                Text(self.todoItem.name)
                    .strikethrough(self.todoItem.isCompleted, color: .black)
                    .bold()
                    .animation(.default)
                Spacer()
                Circle()
                    .frame(width: 20, height: 20)
                    .foregroundColor(self.color(for: self.todoItem.priority))
            }
        }.toggleStyle(CheckboxStyle())
    }
    // 根据优先级显示不同颜色
    private func color(for priority: Priority) -> Color {
        switch priority {
        case .high:
            return .red
        case .normal:
            return .orange
        case .low:
            return .green
        }
    }
}
// checkbox复选框样式
struct CheckboxStyle: ToggleStyle {
    func makeBody(configuration: Self.Configuration) -> some View {
        return HStack {
            Image(systemName: configuration.isOn ? "checkmark.circle.fill" : "circle")
                .resizable()
                .frame(width: 24, height: 24)
                .foregroundColor(configuration.isOn ? .purple : .gray)
                .font(.system(size: 20, weight: .bold, design: .default))
                .onTapGesture {
                    configuration.isOn.toggle()
                }
            configuration.label
        }
    }
}


//  NewToDoView.swift
import SwiftUI
struct NewToDoView: View {
    @State var name: String
    @State var isEditing = false
    @State var priority: Priority
    @Binding var showNewTask: Bool
    @Binding var todoItems: [ToDoItem]
    var body: some View {
        VStack {
            Spacer()
            VStack {
                TopNavBar(showNewTask: $showNewTask)
                InputNameView(name: $name, isEditing: $isEditing)
                PrioritySelectView(priority: $priority)
                SaveButton(name: $name, showNewTask: $showNewTask, todoItems: $todoItems, priority: $priority)
            }
            .padding()
            .background(Color.white)
            .cornerRadius(10, antialiased: true)
            .offset(y: isEditing ? -320 : 0)
        }.edgesIgnoringSafeArea(.bottom)
    }
}
struct NewToDoView_Previews: PreviewProvider {
    static var previews: some View {
        NewToDoView(name: "", priority: .normal, showNewTask: .constant(true), todoItems: .constant([]))
    }
}
// 顶部导航栏
struct TopNavBar: View {
    @Binding var showNewTask: Bool
    var body: some View {
        HStack {
            Text("新建事项")
                .font(.system(.title))
                .bold()
            Spacer()
            Button(action: {
                //关闭弹窗
                self.showNewTask = false
            }) {
                Image(systemName: "xmark.circle.fill")
                    .foregroundColor(.gray)
                    .font(.title)
            }
        }
    }
}
//输入框
struct InputNameView: View {
    @Binding var name: String
    @Binding var isEditing: Bool
    var body: some View {
        TextField("请输入", text: $name, onEditingChanged: { (editingChanged) in
            self.isEditing = editingChanged
        })
            .padding()
            .background(Color(.systemGray6))
            .cornerRadius(8)
            .padding(.bottom)
    }
}
// 选择优先级
struct PrioritySelectView: View {
    @Binding var priority: Priority
    var body: some View {
        HStack {
            PrioritySelectRow(name: "高", color: priority == .high ? Color.red : Color(.systemGray4))
                .onTapGesture { self.priority = .high }
            PrioritySelectRow(name: "中", color: priority == .normal ? Color.orange : Color(.systemGray4))
                .onTapGesture { self.priority = .normal }
            PrioritySelectRow(name: "低", color: priority == .low ? Color.green : Color(.systemGray4))
                .onTapGesture { self.priority = .low }
        }
    }
}
// 选择优先级
struct PrioritySelectRow: View {
    var name: String
    var color:Color
    var body: some View {
        Text(name)
            .frame(width: 80)
            .font(.system(.headline))
            .padding(10)
            .background(color)
            .foregroundColor(.white)
            .cornerRadius(8)
    }
}
// 保存按钮
struct SaveButton: View {
    @Binding var name:String
    @Binding var showNewTask: Bool
    @Binding var todoItems: [ToDoItem]
    @Binding var priority:Priority
    var body: some View {
        Button(action: {
            //判断输入框是否为空
            if self.name.trimmingCharacters(in: .whitespaces) == "" {
                return
            }
            //添加一条新数据
            self.addTask(name: self.name, priority: self.priority)
            //关闭弹窗
            self.showNewTask = false
        }) {
            Text("保存")
                .font(.system(.headline))
                .frame(minWidth: 0, maxWidth: .infinity)
                .padding()
                .foregroundColor(.white)
                .background(Color.blue)
                .cornerRadius(8)
        }
        .padding([.top,.bottom])
    }
    //添加新事项方法
    private func addTask(name: String, priority: Priority, isCompleted: Bool = false) {
        let task = ToDoItem(name: name, priority: priority, isCompleted: isCompleted)
        todoItems.append(task)
    }
}

快来动手试试吧!



相关文章
|
7月前
|
SQL 存储 开发框架
EntityFramework数据持久化复习资料4、Lambda表达式的使用(重点内容)
EntityFramework数据持久化复习资料4、Lambda表达式的使用(重点内容)
55 0
|
3月前
|
iOS开发 开发者 UED
探索iOS应用开发中的SwiftUI框架
【9月更文挑战第26天】 在iOS开发的海洋中,SwiftUI犹如一艘现代的快艇,引领着开发者们驶向更加高效与直观的编程体验。本文将带你领略SwiftUI的魅力,从其设计理念到实际应用,我们将一步步揭开它如何简化界面构建过程的面纱。通过对比传统方式,你将看到SwiftUI如何让代码变得像诗一样优美,同时保持强大的功能性和灵活性。准备好让你的iOS开发技能加速升级,一起驾驭这股新潮流吧!
|
4月前
|
数据处理 开发者 C#
WPF数据绑定实战:从零开始,带你玩转数据与界面同步,让你的应用程序更上一层楼!
【8月更文挑战第31天】在WPF应用开发中,数据绑定是核心技能之一,它能实现界面元素与数据源的同步更新。本文详细介绍了WPF数据绑定的概念与实现方法,包括属性绑定、元素绑定及路径绑定等技术,并通过示例代码展示了如何创建数据绑定。通过数据绑定,开发者不仅能简化代码、提高可维护性,还能提升用户体验。无论初学者还是有经验的开发者,都能从中受益,更好地掌握WPF数据绑定技巧。
103 0
|
7月前
|
开发框架 前端开发 Swift
SwiftUI的优缺点
SwiftUI的优缺点
230 0
|
7月前
|
编译器 API Swift
【Swift开发专栏】Swift中的SwiftUI框架初探
【4月更文挑战第30天】SwiftUI是苹果2019年推出的界面构建框架,简化iOS应用开发。通过声明式语法和编译器优化,提供直观高效的UI设计。本文将介绍SwiftUI概述、主要特性及实际案例。SwiftUI强调“少即是多”,用少量代码实现复杂界面,提供简洁API、自动布局、双向数据绑定等功能。通过视图组合和实时预览加速开发。案例展示如何用SwiftUI构建用户列表界面,体现其结构清晰、易扩展的优势。SwiftUI在iOS开发中的重要性日益提升。
97 0
|
7月前
|
开发工具 Swift iOS开发
利用SwiftUI构建动态用户界面:iOS开发新范式
【4月更文挑战第3天】 随着苹果不断推进其软件开发工具的边界,SwiftUI作为一种新兴的编程框架,已经逐渐成为iOS开发者的新宠。不同于传统的UIKit,SwiftUI通过声明式语法和强大的功能组合,为创建动态且响应式的用户界面提供了一种更加简洁高效的方式。本文将深入探讨如何利用SwiftUI技术构建具有高度自定义能力和响应性的用户界面,并展示其在现代iOS应用开发中的优势和潜力。
|
7月前
|
SQL 存储 开发框架
EntityFramework数据持久化复习资料6、EntityFramework引入
EntityFramework数据持久化复习资料6、EntityFramework引入
55 0
|
7月前
|
存储 数据库 iOS开发
IOS开发数据存储:什么是 CoreData?如何在应用中使用它?
IOS开发数据存储:什么是 CoreData?如何在应用中使用它?
232 0
|
C# 计算机视觉 开发者
在Winform中一分钟入门使用好看性能还好的Blazor Hybrid
在Winform中一分钟入门使用好看性能还好的Blazor Hybrid
295 0
在Winform中一分钟入门使用好看性能还好的Blazor Hybrid
|
JavaScript 前端开发 中间件
Redux 原理探秘
Redux 是一个非常不错的状态管理库,和 Vuex 不同的是 Redux 并不和 React 强绑定,你甚至可以在 Vue 中使用 Redux。当初的目标是创建一个状态管理库,来提供最简化 API。
141 0