code

SwiftUI의 DSL을 활성화하는 것은 무엇입니까?

codestyles 2020. 12. 5. 09:47
반응형

SwiftUI의 DSL을 활성화하는 것은 무엇입니까?


Apple의 새로운 SwiftUI프레임 워크는 효과적으로 튜플을 빌드하는 새로운 종류의 구문사용 하지만 다른 구문이있는 것 같습니다.

var body: some View {
    VStack(alignment: .leading) {
        Text("Hello, World") // No comma, no separator ?!
        Text("Hello World!")
    }
}

이 구문이 실제로 무엇인지 다루려고 시도하면서VStack 여기에 사용 이니셜 라이저가 유형의 클로저를 () -> Content두 번째 매개 변수로 한다는 것을 알았습니다. 여기에서 클로저를 통해 추론 Content되는 일반 매개 변수는 이에 부합합니다 View. 어떤 유형 Content이 추론 되는지 알아보기 위해 기능을 유지하면서 코드를 약간 변경했습니다.

var body: some View {
    let test = VStack(alignment: .leading) {
        Text("Hello, World")
        Text("Hello World!")
    }

    return test
}

이와 함께, test유형으로 자신을 밝혀 VStack<TupleView<(Text, Text)>>그 의미 Content유형입니다 TupleView<Text, Text>. 를 찾아 보면 TupleView, SwiftUI래핑해야하는 튜플을 전달해야만 초기화 할 수있는 자체 에서 시작된 래퍼 유형이라는 것을 알았습니다 .

질문

이제이 Text예제 의 두 인스턴스가 어떻게 TupleView<(Text, Text)>. 이 해킹되어 SwiftUI있으므로 잘못된 정규 스위프트 구문? 유형이 TupleView되는 것은 SwiftUI이 가정을 뒷받침합니다. 아니면 이것이 유효한 Swift 구문입니까? 그렇다면 외부 에서 어떻게 사용할 수 있습니까?SwiftUI


마틴 말했듯이 당신에 대한 설명서를 보면, VStackinit(alignment:spacing:content:), 당신은 것을 볼 수 있습니다 content:매개 변수는 속성이 있습니다 @ViewBuilder:

init(alignment: HorizontalAlignment = .center, spacing: Length? = nil,
     @ViewBuilder content: () -> Content)

이 속성은 ViewBuilder생성 된 인터페이스를 보면 다음과 같은 유형을 참조합니다 .

@_functionBuilder public struct ViewBuilder {

    /// Builds an empty view from an block containing no statements, `{ }`.
    public static func buildBlock() -> EmptyView

    /// Passes a single view written as a child view (e..g, `{ Text("Hello") }`)
    /// through unmodified.
    public static func buildBlock(_ content: Content) -> Content 
      where Content : View
}

@_functionBuilder속성은 " 함수 빌더 " 라는 비공식적 기능의 일부로 , 여기에서 Swift 진화에 대해 언급했으며 Xcode 11과 함께 제공되는 Swift 버전을 위해 특별히 구현되어 SwiftUI에서 사용할 수 있습니다.

유형을 표시하면 @_functionBuilder함수, 계산 된 속성 및이 경우 함수 유형의 매개 변수와 같은 다양한 선언에서 사용자 정의 속성으로 사용할 수 있습니다. 이러한 주석이 달린 선언은 함수 빌더를 사용하여 코드 블록을 변환합니다.

  • 주석이 달린 함수의 경우 변환되는 코드 블록이 구현입니다.
  • 주석이 달린 계산 된 속성의 경우 변환되는 코드 블록이 게터입니다.
  • 함수 유형의 주석이 달린 매개 변수의 경우 변환되는 코드 블록은 전달되는 클로저 표현식입니다 (있는 경우).

함수 빌더가 코드를 변환하는 방식은 일련의 표현식을 가져와 단일 값으로 통합하는와 같은 빌더 메소드 의 구현에 의해 정의됩니다 buildBlock.

예를 들어 1 ~ 10 개의 준수 매개 변수 ViewBuilder구현 buildBlock하여 View여러 뷰를 하나의 TupleView다음 으로 통합합니다 .

@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
extension ViewBuilder {

    /// Passes a single view written as a child view (e..g, `{ Text("Hello") }`)
    /// through unmodified.
    public static func buildBlock<Content>(_ content: Content)
       -> Content where Content : View

    public static func buildBlock<C0, C1>(_ c0: C0, _ c1: C1) 
      -> TupleView<(C0, C1)> where C0 : View, C1 : View

    public static func buildBlock<C0, C1, C2>(_ c0: C0, _ c1: C1, _ c2: C2)
      -> TupleView<(C0, C1, C2)> where C0 : View, C1 : View, C2 : View

    // ...
}

이를 통해 VStack의 이니셜 라이저에 전달 된 클로저 내의 뷰 표현식 세트를 buildBlock동일한 수의 인수 를 사용하는 호출로 변환 할 수 있습니다 . 예를 들면 :

struct ContentView : View {
  var body: some View {
    VStack(alignment: .leading) {
      Text("Hello, World")
      Text("Hello World!")
    }
  }
}

에 대한 호출로 변환됩니다 buildBlock(_:_:).

struct ContentView : View {
  var body: some View {
    VStack(alignment: .leading) {
      ViewBuilder.buildBlock(Text("Hello, World"), Text("Hello World!"))
    }
  }
}

결과 은폐하는 결과 유형 some View 에 의해 충족되고 TupleView<(Text, Text)>.

당신은 점에 유의하겠습니다 ViewBuilder만 정의 buildBlock우리가 11 파단을 정의하려고 시도 그렇다면, 10 개의 매개 변수를 :

  var body: some View {
    // error: Static member 'leading' cannot be used on instance of
    // type 'HorizontalAlignment'
    VStack(alignment: .leading) {
      Text("Hello, World")
      Text("Hello World!")
      Text("Hello World!")
      Text("Hello World!")
      Text("Hello World!")
      Text("Hello World!")
      Text("Hello World!")
      Text("Hello World!")
      Text("Hello World!")
      Text("Hello World!")
      Text("Hello World!")
    }
  }

이 코드 블록을 처리 할 빌더 메서드가 없기 때문에 컴파일러 오류가 발생합니다 (이 기능은 아직 작업 중이므로 관련 오류 메시지는 그다지 도움이되지 않습니다).

실제로 사람들이이 제한에 자주 부딪 힐 것이라고 생각하지 않습니다. 예를 들어 위의 예는 ForEach대신 뷰를 사용하여 더 잘 제공 될 것입니다.

  var body: some View {
    VStack(alignment: .leading) {
      ForEach(0 ..< 20) { i in
        Text("Hello world \(i)")
      }
    }
  }

그러나 정적으로 정의 된 뷰가 10 개 이상 필요한 경우 뷰를 사용하여이 제한을 쉽게 해결할 수 있습니다 Group.

  var body: some View {
    VStack(alignment: .leading) {
      Group {
        Text("Hello world")
        // ...
        // up to 10 views
      }
      Group {
        Text("Hello world")
        // ...
        // up to 10 more views
      }
      // ...
    }

ViewBuilder 또한 다음과 같은 다른 함수 작성기 메서드를 구현합니다.

extension ViewBuilder {
    /// Provides support for "if" statements in multi-statement closures, producing
    /// ConditionalContent for the "then" branch.
    public static func buildEither<TrueContent, FalseContent>(first: TrueContent)
      -> ConditionalContent<TrueContent, FalseContent>
           where TrueContent : View, FalseContent : View

    /// Provides support for "if-else" statements in multi-statement closures, 
    /// producing ConditionalContent for the "else" branch.
    public static func buildEither<TrueContent, FalseContent>(second: FalseContent)
      -> ConditionalContent<TrueContent, FalseContent>
           where TrueContent : View, FalseContent : View
}

이를 통해 if 문을 처리 할 수 ​​있습니다.

  var body: some View {
    VStack(alignment: .leading) {
      if .random() {
        Text("Hello World!")
      } else {
        Text("Goodbye World!")
      }
      Text("Something else")
    }
  }

다음으로 변환됩니다.

  var body: some View {
    VStack(alignment: .leading) {
      ViewBuilder.buildBlock(
        .random() ? ViewBuilder.buildEither(first: Text("Hello World!"))
                  : ViewBuilder.buildEither(second: Text("Goodbye World!")),
        Text("Something else")
      )
    }
  }

( ViewBuilder.buildBlock명확성 위해 중복 1 인수 호출을 냄).


유사한 내용은 DSL에 관한 섹션의 Swift WWDC 비디오의 새로운 기능 (~ 31 : 15에서 시작)에 설명되어 있습니다. 이 속성은 컴파일러에 의해 해석되고 관련 코드로 변환됩니다.

여기에 이미지 설명 입력

참고 URL : https://stackoverflow.com/questions/56434549/what-enables-swiftuis-dsl

반응형