1

由于 SwiftUI 及其将方法分解为括号的能力(感谢函数生成器),如下所示:

struct ContentView : View {
    var body: some View {
        VStack {
            Text("Hello World!")
            Text("by Purple Giraffe").color(.gray)
        }

    }

函数构建器代码只是为了强调分解可以很方便的事实。我不希望它帮助我进行分解分配。

我想知道是否可以将分配(带有其他东西)分解成括号,如下所示:

struct AnimationViewConfiguration {
    var contentMode:Int = 0
    var mainTitle:String = "test"
    var subTitle:String = ""
    var alternativeSubtitle:String = ""
    var numberOfIteration:Int = 0
    var frameRate = 40
    var maximumSimultaneousParalax:Int = 5
    var minimumSimultaneousParalax:Int = 2
}

class someViewController: UIViewController {
    var mainBackgroundAnimationViewConfig = AnimationViewConfiguration()

    func animateBackground(_ useAlternativeBackground:Bool) {
        // The normal bulky way
        if useAlternativeBackground == false {
            mainBackgroundAnimationViewConfig.contentMode = 3
            mainBackgroundAnimationViewConfig.mainTitle = "Your super animation"
            mainBackgroundAnimationViewConfig.subTitle = "A subtitle anyway"
            mainBackgroundAnimationViewConfig.alternativeSubtitle = "Hey another one!"
            // partial or complete assignation
            // mainBackgroundAnimationViewConfig.numberOfIteration = 4
            mainBackgroundAnimationViewConfig.frameRate = 40
            mainBackgroundAnimationViewConfig.maximumSimultaneousParalax = 19
            mainBackgroundAnimationViewConfig.minimumSimultaneousParalax = 3
        } else {
            // The way I'd like it to be
            mainBackgroundAnimationViewConfig with { // imaginary `with` keyword
                contentMode = 0
                mainTitle = "Your super animation"
                subTitle = "A subtitle anyway"
                alternativeSubtitle = "Hey another one!"
                // partial or complete assignation
                // numberOfIteration = 4
                frameRate = 40
                maximumSimultaneousParalax = 19
                minimumSimultaneousParalax = 3
            }
        }
    }
}

关键是要避免重复长变量名 15 次,因为您知道您经常使用 2,3,4 缩进来执行此操作(这使得抓住眼睛更加烦人)。

对于建议将其放在特定函数中的人,我想说出于同样的原因,我们有时会使用匿名函数(即只使用一次......),在不制作更多样板的情况下进行分配仍然很方便。


感谢@matt 提到with其他语言中用于此目的的关键字~

如果它不存在,它会在 swift5.1+ 中出现吗?

你不觉得它很方便吗?

4

4 回答 4

2

你不觉得它很方便吗?

不,有什么问题

var test = Test(
                a : 3,  
                c : 4, 
                s : "hey" 
            )

开始?这会将其他属性保留为默认值。

或者,如果你稍后发生变异,

test.c = 4
test.a = 3
test.s = "hey"

或者

(test.c, test.a, test.s) = (4, 3, "hey")

? 我不明白如何需要另一层语法糖。有些语言使用的with构造可以完成您描述的那种事情(通过单个引用而不是每次都显式的点表示法分配属性),但我并不渴望在 Swift 中使用它。


编辑后编辑:如果它只是您反对的长名称,则复制到一个短名称的临时变量,设置所需的属性,然后复制回来:

var thisIsAReallyLongName = Whatever()
do {
    var temp = thisIsAReallyLongName
    temp.c = 4
    temp.a = 3
    temp.s = "hey"
    thisIsAReallyLongName = temp
}
于 2020-02-06T15:33:47.883 回答
1

最接近你想要的是

struct Test {
    var a:Int = 0
    var b:Int = 0
    var s:String = ""
    mutating func update(_ closure: (inout Int, inout Int, inout String)->Void)->Void {
        closure(&a, &b, &s)
    }
}

var test = Test()

test.update { (a, b, c) in
    a = 10
    b = 20
    c = "Alfa"
}

或更好(支持部分更新,xcode 为您提供可用的内容)

struct Test {
    var a:Int = 0
    var b:Int = 0
    var s:String = ""
    mutating func update(_ closure: (inout Self)->Void)->Void {
        closure(&self)
    }
}

var test = Test()

test.update { (v) in
    v.a = 20
    v.s = "Alfa"
}

更新:谁知道未来?

迅捷新功能

您可以调用声明 func callAsFunction 方法的类型的值,如函数。调用语法是应用 func callAsFunction 方法的简写。

struct Adder {
    var base: Int

    func callAsFunction(_ x: Int) -> Int {
      return x + base
    }
}

var adder = Adder(base: 3)
adder(10) // returns 13, same as 
adder.callAsFunction(10)

您必须在调用站点包含 func callAsFunction 参数标签。您可以在单个类型上添加多个 func callAsFunction 方法,并且可以将它们标记为变异。func callAsFunction 与 throws 和 rethrows 以及尾随闭包一起使用。(59014791)

于 2020-02-07T14:12:42.250 回答
1

我自己也遇到过同样的问题,这就是我发现你的问题的方式(对不起,如果你从那以后继续前进)。

我首先认为 SwiftUI 会建议一个答案,但它似乎对分配所做的只是将方法链接在一起:Text.color(:)从你的示例(实际上它没有工作,所以我不得不将其更改为Text.foregroundColor(:))是一个返回新 Text 实例的方法新颜色,color甚至可能不是结构的属性,因为它似乎由运行示例后modifiers包含的未记录数组支持。SwiftUI.Text.Modifier.color(Optional(gray))

change(:)所以你的严格要求让我思考,最后我找到了一个相当简单的解决方法,其中包括向你的结构添加一个(公共)方法:

mutating func change(_ closure: (inout AnimationViewConfiguration) -> Void) {
        closure(&self)
    }

这将允许以下语法,我认为这非常接近我们的要求:

var mainBackgroundAnimationViewConfig = AnimationViewConfiguration()

mainBackgroundAnimationViewConfig.change {
    $0.contentMode = 0
    $0.mainTitle = "Your super animation"
    $0.subTitle = "A subtitle anyway"
    $0.alternativeSubtitle = "Hey another one!"

    // partial or complete assignation
    // numberOfIteration = 4
    $0.frameRate = 40
    $0.maximumSimultaneousParalax = 19
    $0.minimumSimultaneousParalax = 3
}

除非我遗漏了什么,否则这应该可以工作……当然,您可以在其中运行任意代码,如果需要,您需要self在调用者站点上明确引用;并且可能有一些关于保留周期的警告需要注意,但乍一看,除了标准的关闭警告之外,我没有看到任何东西。但请务必指出您可能发现的任何问题!

显然,此版本仅适用于可变属性:尝试更改let常量将无法编译,无论结构本身是常量还是它的任何属性。对于前一种情况,您可以返回修改后的副本:

func copyWithChanges(_ closure: (inout AnimationViewConfiguration) -> Void) -> AnimationViewConfiguration {
    var copy = self
    closure(&copy)
    return copy
}

// call it like this:

let newVersionOfTheConfig = mainBackgroundAnimationViewConfig.copyWithChanges {
    $0.contentMode = 12
    // etc.
}

对于类,无论它们是变量还是常量,它甚至更简单(如果我们仍然假设它们的所有属性都是可变的),因为闭包将获得对它可以随意修改的实例的引用:

func change(_ closure: (AnimationViewConfiguration2) -> Void)  {
    closure(self)
}

编辑:Swift 5.2开始,您可以命名change(_:)函数callAsFunction(_:)并在调用站点上保存一些输入:

// inside the struct declaration:
mutating func callAsFunction(_ closure: (inout AnimationViewConfiguration) -> Void) {
        closure(&self)
    }
// ...
// using the struct:

var mainBackgroundAnimationViewConfig = AnimationViewConfiguration()

mainBackgroundAnimationViewConfig {
    $0.contentMode = 0
    $0.mainTitle = "Your super animation"
    $0.subTitle = "A subtitle anyway"
    $0.alternativeSubtitle = "Hey another one!"
}

结束编辑

然而,如果你想使用这个语法来初始化一个实例,或者为它的任何不可变属性创建一个带有新值的副本,老实说,即使我们试图重新-使用“延迟初始化”属性包装器(该功能提案中的示例用例)实现不变性,但这似乎超出了范围:我们在这里只提到了已经初始化的实例和可变属性。

于 2020-05-09T22:02:19.063 回答
0

我觉得你有点糊涂了。这里有几个概念在起作用,函数构建器并没有你想象的那么好。

当代码放在两个大括号之间时,我们称之为代码。这是帮助组织程序的好方法,因为我们可以将相关的顺序逻辑部分放在一起。代码块最常出现的地方是函数中。例如,我有一个计算用户工资的函数:

func calculateSalary() {
   let monthly = 3500
   let yearlyTotal = monthly * 12
   print("Your salary is \(yearlyTotal)")
}

然后我们可以像这样调用函数:

calculateSalary()

但是,我们也可以将此函数分配给一个变量,我们将其称为闭包。(函数和闭包实际上是一回事,函数只是命名的闭包,我们可以轻松地重用和调用)。

let calculateSalary: () -> Void = {
   let monthly = 3500
   let yearlyTotal = monthly * 12
   print("Your salary is \(yearlyTotal)")
}

如您所料,我们可以以完全相同的方式调用该函数。

这意味着我们可以将闭包作为参数传递给方法或初始化程序,以便另一个函数或初始化程序可以调用我们传入的函数。例如:

/** 
 * this is a stupid, pointless class (but it demonstrates
 * closures-as-parameters nicely)
 */

class SalaryCalculator {

    let closure: () -> Void

    init(closure: () -> Void) {
        self.closure = closure
    }

}

let calculator = SalaryCalculator(closure: {
   let monthly = 3500
   let yearlyTotal = monthly * 12
   print("Your salary is \(yearlyTotal)")
})

// call the closure!
calculator.closure()

这也可以使用尾随闭包语法编写,如果闭包是我们的初始化程序(或函数)的最后一个参数,我们可以删除参数标签:

let calculator = SalaryCalculator {
   let monthly = 3500
   let yearlyTotal = monthly * 12
   print("Your salary is \(yearlyTotal)")
}

// call the closure!
calculator.closure()

当代码似乎被封装在大括号中时,这就是您所看到的(因式分解不是用来描述这一点的术语)。它实际上是一个传递给底层类型初始化器的闭包参数。

SwiftUI 中的函数构建器使用相同的原理,但它比我现在想介绍的要微妙和复杂一些。

所以,为了更直接地回答你的问题——如果你想把你的代码分解成更易于管理的部分,我会创建一个classor来封装一些逻辑,然后将顺序相关的调用分解成新的orstruct上的函数。我不确定您为什么要以问题中显示的方式进行分配,这实际上没有任何意义,尤其是 Swift 的初始化程序提供了如此多的可定制性。classstruct

于 2020-02-06T14:40:23.563 回答