क्या SwiftUI के DSL को सक्षम करता है?


89

ऐसा लगता है कि 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प्रकार का होना इस धारणा का समर्थन करता है। या यह वैध स्विफ्ट सिंटैक्स है? यदि हाँ, तो कोई इसका उपयोग कैसे कर सकता है ?SwiftUI


3
developer.apple.com/documentation/swiftui/vstack/3278367-init दिखाता है कि एक "कस्टम विशेषता" @ViewBuilder हैdeveloper.apple.com/documentation/swiftui/viewbuilder
मार्टिन आर

1
स्विफ़्ट फ़ोरम में यहाँ पर चर्चा की गई है। फ़ोरम .swift.org/t/pitch - introduce - custom - attributes/21335 और यहाँ फ़ोरम
मार्टिन आर

जवाबों:


111

मार्टिन कहते हैं , अगर आप के लिए दस्तावेज़ को देखने VStackके init(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विशेषता " फ़ंक्शन बिल्डरों " नामक एक अनौपचारिक विशेषता का एक हिस्सा है , जिसे यहां स्विफ्ट विकास पर पिच किया गया है , और स्विफ्ट के संस्करण के लिए विशेष रूप से कार्यान्वित किया गया है जो कि Xcode 11 के साथ जहाजों को स्विफ्टयूआई में उपयोग करने की अनुमति देता है।

एक प्रकार को चिह्नित करना @_functionBuilderइसे विभिन्न घोषणाओं जैसे कि फ़ंक्शन, गणना किए गए गुणों और, इस मामले में, फ़ंक्शन प्रकार के मापदंडों पर एक कस्टम विशेषता के रूप में उपयोग करने की अनुमति देता है। ऐसी एनोटेट घोषणाएं कोड के ब्लॉक को बदलने के लिए फ़ंक्शन बिल्डर का उपयोग करती हैं:

  • एनोटेट किए गए कार्यों के लिए, कोड का ब्लॉक जो रूपांतरित हो जाता है, वह है कार्यान्वयन।
  • एनोटेट कंप्यूटेड संपत्तियों के लिए, कोड का ब्लॉक जो रूपांतरित हो जाता है, वह है।
  • फ़ंक्शन प्रकार के एनोटेट किए गए मापदंडों के लिए, कोड का ब्लॉक जो रूपांतरित हो जाता है, वह कोई क्लोजर एक्सप्रेशन है जो इसे (यदि कोई हो) पास किया जाता है।

जिस तरह से एक फ़ंक्शन बिल्डर कोड को परिवर्तित करता है , उसे बिल्डर तरीकों के कार्यान्वयन द्वारा परिभाषित किया जाता है buildBlock, जैसे कि अभिव्यक्ति का एक सेट लेता है और उन्हें एक एकल मूल्य में समेकित करता है।

उदाहरण के लिए, ViewBuilderऔजार buildBlock1 से 10 के लिए 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केवल buildBlock10 मापदंडों को परिभाषित करता है , इसलिए यदि हम 11 साक्षात्कारों को परिभाषित करने का प्रयास करते हैं:

  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
}

यदि यह कथन को संभालने की क्षमता देता है:

  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")
      )
    }
  }

(निरर्थक 1-तर्क का उत्सर्जन ViewBuilder.buildBlockस्पष्टता के लिए कहता है )।


4
ViewBuilderकेवल buildBlock10 मापदंडों को परिभाषित करता है - इसका मतलब यह है कि var body: some View11 से अधिक उप-साक्षात्कार नहीं हो सकते हैं?
लिनसग्राफर्थ

1
@LinusGeffarth वास्तव में मुझे नहीं लगता कि लोग इस प्रतिबंध में भाग लेंगे कि अक्सर, क्योंकि वे संभवत: ForEachइसके बजाय दृश्य जैसे कुछ का उपयोग करना चाहते हैं । हालाँकि आप Groupइस प्रतिबंध को हटाने के लिए दृश्य का उपयोग कर सकते हैं , मैंने यह दिखाने के लिए अपना उत्तर संपादित कर दिया है।
हमीश

3
@MandisaW - आप अपने खुद के विचारों में समूह बना सकते हैं और उनका पुन: उपयोग कर सकते हैं। मुझे इसमें कोई समस्या नहीं दिख रही है। मैं इस समय वास्तव में WWDC में हूं, और स्विफ्टयूआई लैब में इंजीनियरों में से एक के साथ बात की है - उन्होंने कहा कि अभी स्विफ्ट की एक सीमा है, और वे एक समझदार-ईश संख्या के रूप में 10 के साथ गए। एक बार वैरिएडिक जेनेरिक को स्विफ्ट में पेश कर दिए जाने के बाद, हम जितने चाहें उतने ”सबव्यू” ले पाएंगे।
२०:२y बजे लॉसियोटी

1
शायद और अधिक दिलचस्प है, बिल्डएयर तरीकों की बात क्या है? ऐसा लगता है कि आपको दोनों को लागू करने की आवश्यकता है, और दोनों के पास एक ही रिटर्न प्रकार है, क्यों वे प्रत्येक प्रश्न में केवल टाइप वापस नहीं करते हैं?
गुसुतफु


13

DSLS के बारे में अनुभाग में WWF वीडियो में व्हाट्स न्यू में एक समरूप चीज का वर्णन किया गया है (~ 31: 15 से शुरू होता है)। संकलक द्वारा विशेषता की व्याख्या की जाती है और संबंधित कोड में इसका अनुवाद किया जाता है:

यहाँ छवि विवरण दर्ज करें

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.