本文将讨论服务器驱动的UI,使用称为UIComponents的可重用组件的实现,以及创建用于呈现UI组件的通用垂直列表视图。 最后将简要讨论UI组件如何实现不同的目的。

什么是服务器驱动的UI?

这是服务器决定需要在应用程序屏幕上呈现的UI视图的体系结构。
应用程序和服务器之间存在合同。 该合同的基础使服务器可以控制应用程序的UI。

那是什么合同?-服务器定义组件列表。 对于服务器上定义的每个组件,我们在应用程序(UIComponent)中都有一个相应的UI实现。 考虑像Hotstar这样的娱乐应用,其合同定义如下。 左边是服务器中的组件,右边是相应的UI组件。



SwiftUI背景设置圆角 swiftui 组件_python


构建服务器驱动的UI组件


工作中-屏幕上没有预定义的布局,例如情节提要。 而是由一个普通的列表视图组成,根据服务器响应,该列表视图垂直呈现多个不同的视图。 为了使其成为可能,我们必须创建独立的视图,并且可以在整个应用程序中重复使用这些视图。 我们将这些可重用视图称为UIComponent。

合同-对于每个服务器组件,我们都有一个UIComponent。

SwiftUI

Swift是一个UI工具包,可让您以编程的声明性方式设计应用程序屏幕。

struct NotificationView: View {
    
    let notificationMessage: String
    
    var body: some View {
        Text(notificationMessage)
    }
}

SwiftUI中服务器驱动的UI实施
这是一个三步过程。

  • 定义独立的UIComponent。
  • 根据API响应构造UIComponent。
  • 在屏幕上呈现UIComponents。

1.定义独立的UIComponents


SwiftUI背景设置圆角 swiftui 组件_python_02


定义独立的UIComponents


输入:首先,要让UIComponent呈现自身,应为其提供数据。
输出:UIComponent定义其UI。 当用于在屏幕内渲染时,它根据提供给它的数据(输入)进行渲染。
UIComponent实现

protocol UIComponent {
    var uniqueId: String  { get }
    func render() -> AnyView
}

所有UI视图都必须符合此UI组件协议。
由于组件是在通用垂直列表中呈现的,因此每个UIComponent必须独立标识。 theuniqueId属性用于实现此目的。
render()是定义组件的UI的位置。 在屏幕上调用此函数将渲染组件。 让我们看一下NotificationComponent。

struct NotificationComponent: UIComponent {
    var uniqueId: String
    
    // The data required for rendering is passed as a dependency
    let uiModel: NotificationUIModel
    
    // Defines the View for the Component
    func render() -> AnyView {
        NotificationView(uiModel: uiModel).toAny()
    }
}

// Contains the properties required for rendering the Notification View
struct NotificationUIModel {
    let header: String
    let message: String
    let actionText: String
}

// Notification view takes the NotificationUIModel as a dependency
struct NotificationView: View {
    let uiModel: NotificationUIModel
    var body: some View {
        VStack {
            Text(uiModel.header)
            Text(uiModel.message)
            Button(action: {}) {
                Text(uiModel.actionText)
            }
        }
    }
}

NotificationUIModel是组件呈现所需的数据。 这是UIComponent的输入。
NotificationView是一个SwiftUI视图,用于定义组件的UI。 它以NotificationUIModel作为依赖项。 当用于在屏幕上呈现时,此视图是UIComponent的输出。

2.根据API响应构造UIComponents

class HomePageController: ObservableObject {
 
    let repository: Repository
    @Published var uiComponents: [UIComponent] = []
  
    ..
    .. 
    
    func loadPage() {
        val response = repository.getHomePageResult()
        response.forEach { serverComponent in
          let uiComponent = parseToUIComponent(serverComponent)
          uiComponents.append(uiComponent)
        }
    }
}

func parseToUIComponent(serverComponent: ServerComponent) -> UIComponent {
  var uiComponent: UIComponent
  
  if serverComponent.type == "NotificationComponent" {
    uiComponent = NotificationComponent(serverComponent.data, serverComponent.id)
  }
  else if serverComponent.type == "GenreListComponent" {
    uiComponent = GenreListComponent(serverComponent.data, serverComponent.id)
  }
  ...
  ...
  return uiComponent
}

HomePageController从存储库加载服务器组件,并将它们转换为UIComponents。
uiComponent的属性负责保存UIComponents的列表。 用@Published属性对其进行包装使其可观察。 其值的任何更改都将发布到Observer(View)。 这样可以使View与应用程序状态保持同步。

3.在屏幕上渲染UIComponents
这是最后一部分。 屏幕的唯一责任是渲染UIComponent。 它订阅了可观察到的uiComponents。 每当uiComponents的值更改时,都会通知HomePage,然后更新其UI。 通用ListView用于呈现UIComponent。

struct HomePageView: View {
    
    @ObservedObject var controller: HomePageViewModel
    
    var body: some View {
    
        ScrollView(.vertical) {
            VStack {
                ForEach(controller.uiComponents, id: \.uniqueId) { uiComponent in
                    uiComponent.render()
                }
            }
        }
        .onAppear(perform: {
            self.controller.loadPage()
        })
        
    }
}

通用Vstack:使用内部的VStack垂直呈现所有UIComponent。 由于UIComponent是唯一可识别的,因此我们可以使用ForEach构造进行渲染。
由于所有符合UIComponent协议的组件都必须返回公共类型,因此render()函数将返回AnyView。 下面是View的扩展,用于将其转换为AnyView。

extension View {
    func toAny() -> AnyView {
        return AnyView(self)
    }
}

结论

我们看到了如何使用UIComponent来赋予服务器对应用程序UI的控制权。 但是,使用UIComponents可以实现更多目标。
让我们考虑一个没有服务器驱动的用户界面的情况。 UI片段在整个应用程序中经常使用多次。 这导致视图和视图逻辑的重复。 因此,最好将UI分成有意义的,可重复使用的UI组件。
以这种方式拥有它们将使域层/业务层定义和构造UI组件。 此外,业务层可以承担控制UI的责任。