使用Apple最新的Swift UI技术编写更好的app(6)

今天要说三个内容:Property Observers、@State、Animation

  1. Property Observers

属性观察本质上就是看到变量发生变化时就执行一段代码。语法看起来和计算属性非常相似,但完全不相同

var isFaceUp: Bool {
willSet {
        if newValue {
            startUsingBonusTime()
        } else {
            stopUsingBonusTime()
        }
    }
}

在这里面,newValue 是个特殊的变量,是新值,是那个将要被设置或获取的值。还有一个didSet方法,里面有一个特殊变量是 oldValue,是旧值,是之前用的值。

  1. @State

View 是 只读的。

事实证明,所有的视图结构都完完全全是只读的。不管SwiftUI使用什么变量持有你的views都是let,因此除了初始化view时的变量外,使用var是没有意义的。也就是只有let或者只读的计算属性才有意义。

为什么呢?

有很多设计原因,比如可以可靠的处理变化高效的执行重绘,视图大多时候应该是无状态的,只需一直绘制它的model即可。他们不需要自己的状态,因此不需要设置成不是只读的。等等。

那么,当view需要状态的时候呢?

事实上在极少的情况下视图需要一些状态的。这样的变量总是临时的。所有的永久状态都应当属于model。

比如说,你已进入“编辑模式”,并正在收集更改。 暂时显示了另一个视图以收集信息或通知用户。你希望动画开始,因此您需要设置动画的终点。

你必须使用@State来标记这个临时状态,发生在@state var的变量变更会使view根据需要进行重绘。从这样意义上来说,它就像@ObservedObject

当views需要state时@State var somethingTemporary: SomeType,实际上,这将为此在堆中腾出一些空间。
这样做是因为View结构本身是只读的,还记得吗?并且当只读View重建时,新版本将继续指向它。 换句话说,对View的更改(通过其参数)不会转储该状态。很快,我们将了解这些@事物(例如@Published和@ObservedObject)是什么,但还不是。 现在,只知道您的View中的任何可读写var必须标有@State。
谨慎使用它们。

  1. 动画

什么可以动画?

你只能对已经出现在屏幕上的容器中的视图的修改做动画。“Views in containers that are already on screen ”

哪些修改呢?

  • View的出现和消失
  • 对视图的“可动画化”视图修改器的参数的更改
  • 对形状创建的参数的更改

如何让动画动起来,有两种方式

  • 隐式动画:使用是私有view modifier 的 .animation(Animation)
  • 显示动画:将要修改的内容放在 withAnimation(Animation){}

隐式动画:自动动画。所有的视图修改器(ViewModifier)参数总是动画的。这些改变是根据你指定的曲线和时间进行变化。简单的加上.animation(Animation)意思你要执行自动动画。

Text(“👻 ”)
.opacity(scary ? 1 : 0)
.rotationEffect(Angle.degrees(upsideDown ? 180 : 0)) 
.animation(Animation.easeInOut) // Swift可以推断出动画

现在,不管什么时候发生变化, opacity/rotation 都会进行动画。由于对动画视图修改器的参数的更改具有动画效果,没有使用.animation()opacity/rotation 的变更会立刻从屏幕上显现。

注意⚠️:.animation修饰符不适用于你想用在容器上的想法。 容器只是将.animation修饰符传播到它包含的所有视图里,换句话说,.animation不能像.padding那样工作,而更像.font。

传递给.animation()的参数是一个 Animation 结构体。这个结构体可以让你控制动画,比如 持续时间, 延迟执行时间,重复,曲线等。

动画曲线:是用来控制动画速率等,比如线性、淡入淡出、弹性等。

隐式与显式动画
这些“自动”隐式动画通常不是动画行为的主要来源。 它们通常用于“叶子”(即非容器)视图。
或者,更一般而言,在通常独立于其他视图工作的视图上。
回想一下,你不能为容器视图隐式设置动画(它会传播到内部的视图)。 那是因为在容器中,您开始希望能够协调View的动画。 本质上,包含在一起的一堆View希望一起进行动画处理。
而且,它们可能都将响应某些用户操作或模型更改而一起进行动画处理。 这就是为什么要需要显示动画了。

显式动画创建一个动画会话,在此期间...由于执行代码块而进行的所有合格更改将一起动画化。
您提供要使用的动画(持续时间,曲线等)和代码块。

withAnimation(.linear(duration: 2)) {
// do something that will cause ViewModifier/Shape arguments to change somewhere 
}

显式动画几乎总是包裹在对ViewModel 意图函数里。 它们也包含在修改UI的地方比如进入编辑模式。 处理用户手势的代码不包裹在withAnimation中是很少见的。

显式动画不会覆盖隐式动画。

transitions过渡

过度指定视图如何出现和离开。过度只不过是一对ViewModifiers。一个modifier是对移动视图之前的修改,另一个是移动视图之后的修改。因此,过渡实际上只是“更改视图修饰符的参数”动画的一种形式。非对称过渡具有2对ViewModifier。一对用于显示视图,另一对用于消失视图。示例:视图在显示时会淡入,但在消失时会在屏幕上飞过。

当view出现和离开屏幕时如何指定ViewModifiers使用呢?使用.transition()。比如使用两个内置的过渡: .scale and .identity

ZStack {
    if isFaceUp {
            RoundedRectangle() // default .transition is .opacity 
      Text(“👻 ”).transition(.scale)
    } else {
        RoundedRectangle(cornerRadius: 10).transition(.identity)
} }

如果 isFaceUp 变更了(当ZStack在屏幕上并且正在执行显式动画) 当变为false时,背面会立即出现,文字会缩小至没有,正面会淡出。当为true时,背面会立刻消失。文字会从无放大到有。

不像.animation(), .transition()不会重新分配到容器的内容视图(但是Group和ForEach确实将.transition()分发到其内容视图)。.transition()仅指定ViewModifiers是什么。它不会导致任何动画的发生。换句话说,在这里将单词transition看作是名词,而不是动词。 您正在声明要使用的过渡,而不是导致过渡发生。

过渡不适用于隐式动画,仅适用于显式动画。

Shape和ViewModifier动画
你现在可能已经注意到,所有实际的动画都发生在Shapes和ViewModifiers中。 那么他们如何参与动画呢?
本质上,动画系统将动画持续时间分为几小段。
Shape或ViewModifier可使动画系统知道需要分段的信息。 (例如,我们的“饼形”将要把饼的角度分成几部分。)
动画系统然后告诉Shape / ViewModifier它应该显示的当前片段。 而且Shape / ViewModifier可以确保其代码始终能够反映出这一点。

与动画系统的通信是通过两个变量(双向)进行的。 该变量是Animatable协议中的唯一内容。
想要动画的Shape和ViewModifier必须实现此协议。
var animatableData:Type
类型必须实现协议VectorArithmetic。那是因为它必须能够在动画曲线上分解成小块。类型几乎总是浮点数(Float,Double,CGFloat)。但是还有另一个实现VectorArithmetic的结构称为AnimatablePair。 AnimatablePair将两个VectorArithmetics组合为一个VectorArithmetic!
当然,您可以拥有AnimatablePairs的AnimatablePairs,因此你可以所有你想要的动画。

由于animatableData双向通信,因此它是可读写的var。该变量的设置是动画系统,它告诉Shape / VM绘制哪一块。该变量的获取是动画系统获取动画的起点/终点。通常,这是一个计算得出的变量(尽管不一定必须如此)。
我们可能不希望在Shape / VM代码中使用名称“ animatableData”(我们想使用更能说明数据对我们而言的变量名)。 因此,获取/设置通常只是获取/设置一些其他变量

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,204评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,091评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,548评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,657评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,689评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,554评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,302评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,216评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,661评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,851评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,977评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,697评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,306评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,898评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,019评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,138评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,927评论 2 355