在这里插入图片描述

以下是一个大致的学习计划,将SwiftUI的知识分成12个主题:

  1. SwiftUI 简介和基础语法
  2. 视图和布局
  3. 状态和数据流
  4. 按钮和用户输入
  5. 列表和数据展示
  6. 导航和页面传递
  7. 动画和过渡效果
  8. 手势和交互
  9. 绘制和绘图
  10. 多平台适配
  11. 网络和数据请求
  12. 实际项目实践和高级主题

每个主题可以安排为一个独立的学习单元,你可以根据自己的学习进度调整时间分配。对于每个主题,你可以通过以下方式进行学习:

1. SwiftUI 简介和基础语法

当我们谈论SwiftUI的简介和基础语法时,首先需要了解SwiftUI是什么以及它的特点。

SwiftUI是苹果公司推出的一种用于构建应用程序用户界面的框架。它是基于Swift语言的,具有声明式的语法,可以让开发者更简单、更高效地创建用户界面。

下面是一些SwiftUI的基础语法和概念:

  1. 视图和容器:

    • SwiftUI的核心构建块是视图(View),它代表了应用程序的用户界面组件。
    • 视图可以是简单的,比如文本标签(Text)、按钮(Button),也可以是复杂的,比如列表视图(List)或者表单视图(Form)。
    • 视图可以嵌套在容器视图中,容器可以是垂直布局的容器(如VStack),也可以是水平布局的容器(如HStack)。
  2. 声明式语法:

    • SwiftUI使用声明式语法,这意味着你只需要描述你想要的结果,而不需要手动处理每个界面操作的细节。
    • 相比传统的命令式编程,声明式语法使代码更易读、更易维护。
  3. 预览和实时预览:

    • SwiftUI提供了一个名为“预览”的功能,可以在开发过程中实时查看和调试界面的外观和行为。
    • 预览可以在不启动模拟器或真机的情况下查看不同设备上的界面效果,节省了开发时间。
  4. 数据绑定:

    • SwiftUI支持数据绑定,即将视图与数据进行绑定,当数据变化时,视图会自动更新。
    • 这意味着你可以轻松地处理用户交互、数据的增删改查,而不需要手动更新视图状态。
  5. 修饰符:

    • SwiftUI使用修饰符(Modifier)来更改和定制视图的外观和行为。
    • 修饰符可以链式调用,允许你按顺序应用多个修饰符,从而创建出你想要的效果。

2. 视图和布局

在SwiftUI中,视图(View)是构建应用程序用户界面的基本构建块。视图用于呈现内容、响应用户输入并展示应用程序的外观。

以下是一些关于视图和布局的详细信息:

  1. 内置视图:

    • SwiftUI提供了许多内置的视图,例如Text(文本标签)、Image(图像)、Button(按钮)、TextField(文本输入框)等等。这些视图可以用于构建应用程序的各个部分。
  2. 自定义视图:

    • 除了内置视图,你还可以创建自定义视图,以满足特定的界面需求。自定义视图可以通过组合多个现有视图来创建,也可以通过继承View协议并实现自己的内容来创建。
  3. 布局和容器视图:

    • 布局是指将视图放置在屏幕上的方式。SwiftUI提供了许多容器视图,用于定义视图的布局结构。常用的容器视图包括VStack(垂直布局)、HStack(水平布局)、ZStack(层叠布局)等。
    • 容器视图可以嵌套使用,形成更复杂的布局结构。通过使用这些容器视图,你可以构建出多层次的视图层次结构。
  4. 对齐和间距:

    • SwiftUI提供了对齐和间距的修饰符,以控制视图在容器内的位置关系和距离。
    • 例如,你可以使用alignment修饰符来指定视图在容器中的对齐方式,使用spacing修饰符来指定视图之间的间距。
  5. 响应用户输入:

    • SwiftUI允许你向视图添加手势识别器(Gesture Recognizer)来响应用户的触摸、点击等操作。
    • 你可以使用onTapGesture、onLongPressGesture等方法来添加响应函数,并对用户的输入进行处理。

3. 状态和数据流

在SwiftUI中,状态和数据流是非常重要的概念。它们帮助我们管理和更新应用程序的数据,并确保界面与数据保持同步。

以下是关于状态和数据流的详细讲解:

  1. 状态:

    • 状态是应用程序中的可变数据。它可以是简单的值(如布尔值、整数)或复杂的数据结构(如数组、字典)。
    • 在SwiftUI中,你可以使用@State属性包装器将状态绑定到视图上。这样,当状态发生变化时,视图会自动更新。
    • 通过@State属性包装器,SwiftUI会追踪状态的变化,并确保界面与最新的状态保持同步。
  2. 数据流:

    • 数据流是指数据在应用程序中的传递和响应。在SwiftUI中,数据流是单向的,从父视图向子视图传递。
    • 你可以在父视图中定义数据,并使用@State属性包装器进行管理。然后,通过传递数据到子视图的方式,子视图可以读取和响应这些数据。
    • 为了实现数据流,SwiftUI使用了一些属性包装器,例如@Binding、@StateObject、@ObservedObject等,它们可以实现视图之间的数据共享和更新。

    当我们需要将数据从父视图传递到子视图时,可以使用@State属性包装器和参数传递的方式实现。以下是一个示例:

struct ParentView: View {
    @State private var message = "Hello"

    var body: some View {
        VStack {
            Text(message)
            ChildView(message: $message)
        }
    }
}

struct ChildView: View {
    @Binding var message: String

    var body: some View {
        VStack {
            Text("Child View")
            TextField("Enter message", text: $message)
        }
    }
}

在上面的示例中,我们在ParentView中创建了一个@State属性message,并将其作为参数传递给ChildView。在ChildView中,我们使用@Binding属性包装器标记message属性,以便使其成为一个可编辑的绑定属性。

当我们在ChildView中修改TextField中的文本时,实际上是修改了父视图中的message属性的值。由于message属性被标记为@Binding,所以父视图中的Text视图会自动更新以反映最新的值。

通过这种方式,我们实现了在父视图中定义数据,并将其传递给子视图以响应和更新。这是一种常见的数据传递和响应机制,在复杂的应用程序中非常有用。

  1. 状态管理:
    • 对于复杂的应用程序,仅仅依赖@State可能不够。在这种情况下,你可以使用@StateObject、@ObservedObject等属性包装器来管理状态。
    • 这些属性包装器允许你创建独立的状态对象,并在多个视图中共享这些状态对象,从而实现更复杂的数据管理和更新。

@ObservedObject:

  • @ObservedObject属性包装器用于管理应用程序中的可观察对象(Observable Object)。
  • 可观察对象是一个遵循ObservableObject协议的自定义类,它负责存储和管理应用程序状态的数据。
  • 通过将可观察对象声明为视图的属性,然后在视图中使用@ObservedObject属性包装器进行引用,我们可以实现对可观察对象属性的观察和更新。

以下是一个使用@ObservedObject的简单示例:

class UserData: ObservableObject {
    @Published var name: String = ""
}

struct ContentView: View {
    @ObservedObject var userData = UserData()

    var body: some View {
        TextField("Enter your name", text: $userData.name)
        Text("Hello, \(userData.name)!")
    }
}

在上面的示例中,UserData类是一个可观察对象,其中的name属性使用了@Published属性包装器,使其能够被观察。在ContentView中,我们使用@ObservedObjectuserData属性标记为可观察对象,当name属性发生变化时,视图会自动更新。

@EnvironmentObject:

  • @EnvironmentObject属性包装器使我们能够在视图之间共享和访问全局的可观察对象。
  • 可观察对象通过使用@EnvironmentObject属性包装器在外部的父视图中设置,然后在子视图中进行访问和使用。
  • 这种方法可用于跨多个视图共享和更新数据,而不需要每个视图都显式传递和维护状态对象。

以下是一个使用@EnvironmentObject的示例:

class UserData: ObservableObject {
    @Published var name: String = ""
}

struct ContentView: View {
    @EnvironmentObject var userData: UserData

    var body: some View {
        TextField("Enter your name", text: $userData.name)
        Text("Hello, \(userData.name)!")
    }
}

在上面的示例中,我们创建了一个名为UserData的可观察对象,并在外部环境中设置它(通常在应用程序的顶层)。然后在ContentView中使用@EnvironmentObject属性包装器将该对象注入到视图中,以便在视图内部使用它。

这些是使用@ObservedObject@EnvironmentObject属性包装器进行状态管理的两个示例。通过使用这些属性包装器,我们可以创建独立的、可观察的对象,实现状态的共享和更新,从而使应用程序更具灵活性和可扩展性。希望这些示例能够帮助你理解如何进行状态管理,如果你还有进一步的问题,请随时提问。
4. 事件和动作:

  • 在SwiftUI中,你可以通过添加按钮、手势等来触发事件和动作。
  • 通过使用@State或其他属性包装器,你能够捕获和处理这些事件,并更新相应的状态,从而改变界面的可视化效果。

在SwiftUI中,我们可以通过添加按钮、手势等的方式来触发事件和动作。这些事件和动作可以与@State或其他属性包装器一起使用,以捕获、处理和更新状态,从而改变界面的可视化效果。

  1. 添加按钮事件:

    • 可以使用Button视图来创建按钮,并为按钮添加事件处理程序。
    • 通过在按钮的action闭包中编写处理逻辑,可以定义按钮被点击时要进行的操作。

    这是一个示例,展示了如何在按钮的点击事件中改变状态:

    struct ContentView: View {
        @State private var isButtonTapped = false
    
        var body: some View {
            VStack {
                if isButtonTapped {
                    Text("Button is tapped")
                } else {
                    Text("Button is not tapped")
                }
                Button(action: {
                    isButtonTapped.toggle()
                }) {
                    Text("Tap me")
                }
            }
        }
    }
    

    在上面的示例中,我们创建了一个Button并为其指定了一个action闭包。按钮被点击时,isButtonTapped属性的值将被切换,从而改变了界面上显示的文本。

  2. 添加手势事件:

    • 除了按钮,我们还可以使用手势来触发事件和动作。手势可以识别用户的交互动作,例如轻按、滑动、拖动等。

    这是一个示例,展示了如何使用手势改变状态:

    struct ContentView: View {
        @State private var isDragging = false
    
        var body: some View {
            let dragGesture = DragGesture()
                .onChanged { _ in
                    isDragging = true
                }
                .onEnded { _ in
                    isDragging = false
                }
    
            Circle()
                .frame(width: 100, height: 100)
                .gesture(dragGesture)
                .foregroundColor(isDragging ? .red : .blue)
        }
    }
    

    在上面的示例中,我们创建了一个圆形视图,并为其添加了拖动手势。当手指在视图上拖动时,isDragging属性的值将被设置为true,从而改变了圆形视图的背景颜色。

通过这些示例,我们可以看到如何使用按钮和手势来触发事件和动作,并通过@State属性来改变界面的可视化效果。这使得我们能够与用户进行交互,并根据交互动作来更新应用程序的状态。

状态和数据流是SwiftUI中非常强大的概念,它们帮助构建有交互性和动态性的应用程序。通过合理地使用和管理状态,你可以轻松构建出响应式、流畅的用户界面。

4. 按钮和用户输入

在SwiftUI中,按钮和用户输入是实现用户交互的重要组成部分。你可以使用Button视图创建按钮,并通过处理器闭包对按钮点击事件作出响应。此外,SwiftUI还提供了许多视图用于接收和处理用户输入,例如TextFieldDatePicker等。

以下是关于按钮和用户输入的详细介绍以及示例:

  1. 按钮:

    • 使用Button视图可以创建可点击的按钮。你可以为按钮提供一个标签(例如Text视图)来显示按钮上的内容,并为按钮添加一个action闭包,以在按钮被点击时执行相应的操作。

    示例代码:

    struct ContentView: View {
        @State private var isButtonTapped = false
    
        var body: some View {
            VStack {
                Text(isButtonTapped ? "Button is tapped" : "Button is not tapped")
                Button(action: {
                    isButtonTapped.toggle()
                }) {
                    Text("Tap me")
                }
            }
        }
    }
    

    在上面的示例中,我们创建了一个按钮,当按钮被点击时,isButtonTapped的状态将被切换,从而改变了上方文本的显示。

  2. 用户输入:

    • SwiftUI提供了多个视图用于接收和处理用户的输入,例如TextFieldDatePickerSlider等。你可以通过使用这些视图,结合@State属性包装器,来获取用户的输入值并更新界面。

    示例代码:

    struct ContentView: View {
        @State private var name: String = ""
    
        var body: some View {
            VStack {
                TextField("Enter your name", text: $name)
                Text("Hello, \(name)!")
            }
        }
    }
    

    在上面的示例中,我们创建了一个TextField来接收用户输入的名称。我们将name属性绑定到TextField的文本,并在界面上显示输入的名称。

    当用户在TextField中输入文本时,name属性会自动更新,并且相关的Text视图将显示相应的问候语。

通过使用按钮和用户输入视图,我们能够实现与用户的交互,接收用户的操作和输入,并响应进行相应的操作。这为创建交互式的用户界面提供了简单而强大的工具。

5. 列表和数据展示

列表和数据展示在应用程序中起着重要的作用,它们用于展示和组织大量的数据。在SwiftUI中,你可以使用ListForEach等视图来创建列表,并结合数据源来展示和处理数据。

以下是关于列表和数据展示的详细说明以及示例:

  1. 创建列表:

    • 使用List视图可以创建一个有序的、可滚动的列表。你可以将一个包含多个元素的集合传递给List,并在闭包中定义每个元素的展示方式。

    示例代码:

    struct ContentView: View {
        let fruits = ["Apple", "Banana", "Orange"]
    
        var body: some View {
            List(fruits, id: \.self) { fruit in
                Text(fruit)
            }
        }
    }
    

    在上面的示例中,我们创建了一个简单的水果列表。我们将字符串数组fruits传递给List,并通过闭包来定义每个水果的展示方式。

    每个水果都会作为一个Text视图显示在列表中。

  2. 使用ForEach从数据源创建视图:

    • 除了List,你还可以使用ForEach视图和数据源来创建自定义的列表。ForEach可以接收一个集合,并为集合中的每个元素创建对应的视图。

    示例代码:

    struct ContentView: View {
        struct Fruit {
            let id = UUID()
            let name: String
        }
    
        let fruits = [
            Fruit(name: "Apple"),
            Fruit(name: "Banana"),
            Fruit(name: "Orange")
        ]
    
        var body: some View {
            VStack {
                ForEach(fruits, id: \.id) { fruit in
                    Text(fruit.name)
                }
            }
        }
    }
    

    在上面的示例中,我们创建了一个自定义的Fruit结构体,并在fruits数组中包含多个实例。使用ForEach,我们将每个水果的名称作为一个Text视图进行展示,并根据每个Fruit结构体的唯一标识符来标识视图。

这些示例展示了在SwiftUI中如何使用ListForEach来创建列表,并展示和处理数据。

ListForEach提供了灵活且功能丰富的方式来展示和处理数据,并且可以根据你的需求进行自定义。你可以根据自己的数据结构和需求来选择适合的方式来展示和组织数据。

当使用ListForEach来展示和处理数据时,它们提供了许多灵活且功能丰富的方式。这些方式可以根据你的需求进行自定义,让你能够以不同的方式展示和组织数据。以下是一些示例来展示其灵活性和功能丰富性:

  1. 自定义列表项视图:

    • 你可以使用List配合自定义的列表项视图来展示数据。这样做可以让你完全控制列表项的外观和行为。

    示例代码:

    struct ContentView: View {
        struct Person {
            let id = UUID()
            let name: String
            let age: Int
        }
    
        let people = [
            Person(name: "John", age: 25),
            Person(name: "Alice", age: 30),
            Person(name: "Bob", age: 45)
        ]
    
        var body: some View {
            List(people, id: \.id) { person in
                VStack(alignment: .leading) {
                    Text(person.name)
                        .font(.headline)
                    Text("Age: \(person.age)")
                        .font(.subheadline)
                        .foregroundColor(.gray)
                }
            }
        }
    }
    

    在上述示例中,我们创建了一个自定义的Person结构体,并使用List来展示people数组中的每个个人。自定义列表项视图包含一个姓名和年龄的Text视图,并使用不同的字体和对齐方式。

  2. 动态删除和移动列表项:

    • 你可以使用onDeleteonMove修饰符让用户能够动态删除和移动列表中的项。

    示例代码:

    struct ContentView: View {
        @State private var tasks = ["Task 1", "Task 2", "Task 3"]
    
        var body: some View {
            List {
                ForEach(tasks, id: \.self) { task in
                    Text(task)
                }
                .onDelete { indexSet in
                    tasks.remove(atOffsets: indexSet)
                }
                .onMove { indices, newOffset in
                    tasks.move(fromOffsets: indices, toOffset: newOffset)
                }
            }
            .listStyle(InsetGroupedListStyle())
        }
    }
    

    在上述示例中,我们创建了一个可编辑的任务列表。通过使用onDelete修饰符,用户可以从列表中删除选定的项。通过使用onMove修饰符,用户可以重新排序列表中的项。

这些示例展示了ListForEach提供的灵活性和功能丰富性。通过自定义列表项视图以及使用动态删除和移动的功能,你可以根据自己的需求来展示和处理数据。这种灵活性使得你能够创建出丰富多样的用户界面,并提供更好的用户体验。

6. 导航和页面传递

在应用程序中实现导航和页面传递是常见的需求。在SwiftUI中,你可以使用NavigationViewNavigationLink来实现导航功能,并通过视图之间传递数据来实现页面传递。

以下是有关导航和页面传递的详细说明及示例:

  1. 导航功能:

    • 使用NavigationView视图可以创建一个导航容器,它提供了导航栏和导航层次结构的支持。你可以在NavigationView中包裹其他视图,并使用NavigationLink来定义导航链接。

    示例代码:

    struct ContentView: View {
        var body: some View {
            NavigationView {
                VStack {
                    Text("Home View")
                    NavigationLink(destination: DetailView()) {
                        Text("Go to Detail View")
                    }
                }
                .navigationTitle("Main")
            }
        }
    }
    
    struct DetailView: View {
        var body: some View {
            VStack {
                Text("Detail View")
            }
            .navigationTitle("Detail")
        }
    }
    

    在上述示例中,我们创建了一个简单的导航界面。在NavigationView中,我们将VStack作为主页视图,其中包含一个导航链接NavigationLink。点击链接后,将导航到DetailView

  2. 页面传递:

    • 使用NavigationLink时,你可以通过在目标视图中接收属性来传递数据。这可以通过在NavigationLink的目标视图中使用init方法接收传递的属性来实现。

    示例代码:

    struct ContentView: View {
        var body: some View {
            NavigationView {
                VStack {
                    Text("Home View")
                    NavigationLink(destination: DetailView(name: "John")) {
                        Text("Go to Detail View")
                    }
                }
                .navigationTitle("Main")
            }
        }
    }
    
    struct DetailView: View {
        var name: String
    
        var body: some View {
            VStack {
                Text("Detail View")
                Text("Hello, \(name)!")
            }
            .navigationTitle("Detail")
        }
    }
    

    在上述示例中,我们通过在NavigationView中的NavigationLink中传递名为"John"的属性,在DetailView中接收并显示该属性。

通过实现导航和页面传递,你可以在应用程序中创建多个视图,并使之彼此之间可以通过导航进行转换,同时还可以通过传递数据在视图之间进行交互。

7. 动画和过渡效果

动画和过渡效果可以为应用程序增加生动性和交互性,并提升用户体验。在SwiftUI中,你可以使用内置的动画修饰符以及过渡效果来实现各种动画效果。

以下是关于动画和过渡效果的详细说明:

  1. 基本动画:

    • SwiftUI提供了许多内置的动画修饰符,例如animation()。你可以将其应用于视图上的操作和属性,以在界面上创建动画效果。

    示例代码:

    struct ContentView: View {
        @State private var scale: CGFloat = 1.0
    
        var body: some View {
            Button("Animate") {
                withAnimation {
                    scale += 0.5
                }
            }
            .padding(20)
            .background(Color.blue)
            .foregroundColor(.white)
            .scaleEffect(scale)
        }
    }
    

    在上述示例中,我们创建了一个按钮,当用户点击按钮时,将使用渐变动画将按钮的比例scale增加0.5。通过使用withAnimation修饰符,我们告诉SwiftUI在该状态更改期间应用动画效果。

  2. 过渡效果:

    • SwiftUI提供了一些内置的过渡效果,如transition()matchedGeometryEffect(),以实现在视图之间进行平滑的过渡效果。

    示例代码:

    struct ContentView: View {
        @State private var showRectangle = false
    
        var body: some View {
            VStack {
                if showRectangle {
                    Rectangle()
                        .frame(width: 200, height: 200)
                        .transition(.scale)
                }
    
                Button("Toggle Rectangle") {
                    withAnimation {
                        showRectangle.toggle()
                    }
                }
                .padding(20)
                .background(Color.blue)
                .foregroundColor(.white)
            }
        }
    }
    

    在上述示例中,我们创建了一个按钮以切换一个矩形的显示。当用户点击按钮时,使用渐变的缩放效果(.scale)来显示或隐藏矩形。

通过使用动画修饰符和过渡效果,你可以为应用程序创建各种动态和平滑的视觉效果,从而增强用户体验。

8. 手势和交互

手势和交互是移动应用程序中的重要组成部分,它们使用户能够与应用程序进行直接的、可触摸的交互。在SwiftUI中,你可以使用内置的手势修饰符来实现各种交互行为。

以下是关于手势和交互的详细说明:

  1. Tap手势:

    • onTapGesture手势修饰符可以用于捕获用户点击视图的动作。

    示例代码:

    struct ContentView: View {
        @State private var isTapped = false
    
        var body: some View {
            Circle()
                .frame(width: 100, height: 100)
                .foregroundColor(isTapped ? .blue : .red)
                .onTapGesture {
                    isTapped.toggle()
                }
        }
    }
    

    在上述示例中,我们创建了一个圆圈,并设置其背景颜色为红色。当用户点击圆圈时,使用onTapGesture捕获点击事件,并切换圆圈的背景颜色为蓝色。

  2. 拖动手势:

    • gesture修饰符可以用于给视图添加拖动手势。

    示例代码:

    struct ContentView: View {
        @State private var offset = CGSize.zero
    
        var body: some View {
            Circle()
                .frame(width: 100, height: 100)
                .foregroundColor(.blue)
                .offset(offset)
                .gesture(
                    DragGesture()
                        .onChanged { value in
                            offset = value.translation
                        }
                        .onEnded { _ in
                            offset = .zero
                        }
                )
        }
    }
    

    在上述示例中,我们创建了一个圆圈,并使用offset属性控制该圆圈的位置。通过给圆圈添加gesture修饰符,并使用DragGesture捕获拖动手势,可以使用户能够拖动圆圈来改变其位置。

通过利用手势修饰符,你可以为视图添加各种交互行为,包括点击、拖动、捏取等。这使得用户可以直接、自然地与应用程序进行交互。

9. 绘制和绘图

Drawing and graphics are important techniques in iOS app development that allow you to create custom shapes and visual effects. In SwiftUI, you can achieve drawing and graphics by using the Path and Shape protocol.

Here is a detailed explanation of drawing and graphics:

  1. Drawing basic shapes:

    • The Path type in SwiftUI represents a path, and you can use it to create and combine basic shapes.

    Example code:

    struct ContentView: View {
        var body: some View {
            Path { path in
                path.move(to: CGPoint(x: 50, y: 50))
                path.addLine(to: CGPoint(x: 100, y: 100))
                path.addLine(to: CGPoint(x: 150, y: 50))
            }
            .stroke(Color.blue, lineWidth: 2)
        }
    }
    

    In the above example, we use Path to create a simple triangular shape. By calling the move(to:) and addLine(to:) methods, we specify the vertices of the triangle. Finally, we use the stroke() modifier to render the shape with a blue border.

  2. Creating custom shapes:

    • In SwiftUI, you can also create custom shapes by implementing the Shape protocol.

    Example code:

    struct ContentView: View {
        var body: some View {
            Triangle()
                .fill(Color.red)
                .frame(width: 200, height: 200)
        }
    }
    
    struct Triangle: Shape {
        func path(in rect: CGRect) -> Path {
            var path = Path()
            path.move(to: CGPoint(x: rect.midX, y: rect.minY))
            path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY))
            path.addLine(to: CGPoint(x: rect.minX, y: rect.maxY))
            path.closeSubpath()
            return path
        }
    }
    

    In the above example, we create a custom triangle shape by implementing the Shape protocol. In the path(in:) method, we use the Path type to create a path with three vertices and close the path with the closeSubpath() method. Finally, we add the custom shape to the view hierarchy and set the fill color using the fill() modifier.

By utilizing drawing and graphics, you can create graphics with custom shapes and visual effects. This allows you to implement various interesting and unique interface designs.

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐