SwiftUI 中的 @State 与 @Binding 和 @ObservableObject 与 @ObservedObject 以及全局 @EnvironmentObject 的极简示例

SwiftUI 提供了一套强大的属性包装器,帮助管理状态和数据流。以下是这几种属性包装器的定义和用法。

  1. @State 和 @Binding:

• @State

@State 是一个属性包装器,用于在 SwiftUI 视图中声明本地状态。当 @State 变量的值改变时,它将使视图失效,并促使视图重新渲染。它通常用于简单的本地组件状态,这些状态不需要共享给其他视图。

例如:

import SwiftUI

struct Content: View {
    @State private var isToggled = false

    var body: some View {
        Toggle("Switch", isOn: $isToggled)
    }
}

• @Binding

@Binding 是一个属性包装器,它提供了对一个 @State 属性的引用,或者其它 @Binding 属性的双向绑定。它允许不同的视图共享对同一状态的访问,并能相互更新状态。

例如,使用 @Binding 将状态传递到子视图:

import SwiftUI

struct Content: View {
    @State private var isToggled = false

    var body: some View {
        ChildView(isToggled: $isToggled)
    }
}

struct ChildView: View {
    @Binding var isToggled: Bool

    var body: some View {
        Toggle("Switch", isOn: $isToggled)
    }
}
  1. @ObservableObject 和 @ObservedObject:

• @ObservableObject

这是一个协议,当遵从该协议的类的某些属性被标记为 @Published 时,这些属性在发生变化会通知其订阅者。通常用于定义外部的数据模型或状态,这些模型或状态将被多个视图访问和修改。

import Combine
import SwiftUI

class MyModel: ObservableObject {
    @Published var isToggled = false
}

struct Content: View {
    @ObservedObject var model = MyModel()

    var body: some View {
        Toggle("Switch", isOn: $model.isToggled)
    }
}

• @ObservedObject

这个属性包装器用于声明一个视图要观察的 ObservableObject 实例。当这个实例的 @Published 属性发生变化时,它会重新渲染视图。

  1. @EnvironmentObject:

@EnvironmentObject 是一个属性包装器,用于从环境中获取一个由某个父视图提供的 ObservableObject 实例。这对于跨多个视图共享数据模型非常有用,不必显式地将模型一层一层传递下去。

例如,在根视图中提供环境对象,并在子视图中访问它:

import Combine
import SwiftUI

class SharedModel: ObservableObject {
    @Published var isToggled = false
}

struct Content: View {
    @StateObject var model = SharedModel()

    var body: some View {
        ChildView()
            .environmentObject(model)
    }
}

struct ChildView: View {
    @EnvironmentObject var model: SharedModel

    var body: some View {
        Toggle("Switch", isOn: $model.isToggled)
    }
}

使用 @EnvironmentObject 之前,需要确保在视图层级中的某个点已经往环境中注入了对象,通常是在视图树的顶层进行。如果在环境中找不到相应的对象,那么试图访问 @EnvironmentObject 会导致运行时错误。

如何将 Xcode 中 Vim 模式下的 JK 按键映射到 ESC 按键

自从 Xcode 推出 Vim 模式,我就研究了一下如何在 Xcode 中开启。

有趣的是我在日常使用中并不会将 Xcode 作为常用编辑器。甚至可以更绝对一些,日常工作学习中几乎就不会使用 Xcode。一来是因为 Xcode 太「重」,启动很慢。二来也不开发 iOS App。开启 Vim 模式的原因仅仅是因为好奇,加上日常自己写代码使用 Vim 较多,所以就想感受一下在 Xcode 中使用 Vim 是怎样一种体验。

对于 Vim 的使用,虽然会有很多快捷键,但使用最频繁的就是 ESC 按键。我在自己的 Vim 设置中,就是将 ESC 按键映射到了 JK 两个按键,也就是连续快速点击「J」和「K」就会触发 ESC 按键。

但 Xcode 并没有提供 Vim 模式下的自定义按键的映射功能。最近重新学习 iOS 开发,对于 ESC 按键的映射实在忍无可忍。终于让我找到了一种「曲线救国」的方案。

实现这个功能的是一款 macOS 系统下的开源键盘映射软件,名字叫「Karabiner-Elements」。

首先,直接到官网下载最新的版本。

接着,按照官方文档的截图示意进行授权安装。安装过程包括开启对应的系统权限,相应的根据提示开启即可。官方文档的步骤很详细,还配了截图,步骤不算多,很快就能设置完。

安装完成之后,本地启动「Karabiner-Elements」App,点击窗口左侧的「Complex Modifications」,如下图所示:

点击右侧设置界面顶部的「Add your own rule」按钮,将弹窗里的默认配置删除。打开 Gist 页面,复制里边的 json 配置信息,将其原封不动的粘贴到弹窗里,点击弹窗右上方的「Save」按钮,保存配置。完成之后即可看到一条名为「jk to escape for xcode」的配置。

此时,在Xcode 中开启 Vim 模式(如果已经是开启状态,可以直接体验),即可享有 「J」「K」 按键映射到 ESC 按键的丝滑体验。

Gist 中的配置是我根据网上找到的一份配置稍微进行了的修改,如果想自行修改配置,可参考官方配置手册

如何在 Xcode 点击 command+S 保存时,自动对 Swift 代码进行格式化

开始学习 SwiftUI,发现 Xcode 貌似没有自动格式化的功能。网上找了一些资料,有些教程里的截图已经过时了,简单摸索后,重新总结了一下。

完成此功能,一共需要三步:

  1. 安装 SwiftFormat-for-xcode 插件
brew install --cask swiftformat-for-xcode
  1. 系统设置

插件安装完成后,需要先进行系统设置,路径如下:

系统设置 -> 隐私与安全性 -> 扩展 ->Xcode Source Editor

点击 Xcode Source Editor,出现一个弹窗,将 SwiftFormat勾选上。

此时,重启 Xcode,完成后会在 Xcode 的 Editor 选项下方出现 SwiftFormat 选项,说明 SwiftFormat 已经安装完成。

但如果想在 Xcode 点击 command + s 保存的时候执行格式化,还需要最后一步设置。

  1. keyboard 快捷键设置

按照如下路径,从系统设置进入:路径如下:

系统设置 -> 键盘 -> 键盘快捷键(键盘导航下方) -> App 快捷键

点击窗口右侧的 + 按钮,如下图所示添加快捷键:

其中 「Format File」可以根据你喜好编辑为「Format Selection」或「Lint File」,点击「完成」即全部设置完毕。

此时,在 Xcode 中编辑代码后,点击 command + S 保存时,会自动执行 Xcode 中 SwiftFormat 功能。

有趣的 Bret Victor

大概在十二年前,也就是 2012 年左右,在 Vimeo 上看了 Bret Victor 的几次演讲视频,其中包括著名的《Inventing on Principle》。

当时给我的理解认知,仅仅停留在 UI 设计以及创意的层面,对背后的核心理念完全没有感知。对于这一点的佐证,也是因为我在搜索 Bret 相关中文内容的时候,无意间搜到了当年 Coolshell 上的一篇文章,在文章的下方,竟然惊奇的发现了我的留言。坦白说,我不太喜欢在网上给人留言,如果真的需要沟通,大概率会发邮件。不过这更加侧面印证了当年的演讲内容确实触动到了我,但很可惜,也只停留在看得见的 UI 设计层面,对于背后的理念,完全没有意识。

时隔十二年,因为今年计划尝试开发自己的产品,所以又想到了当年的 Bret Victor。这一次,有了不一样的理解。

十二年前第一次访问他的网站1的时候,完全没有理解网站的导航为什么会是那样设计?而且还划分了「章节」,搞的一头雾水,以为仅仅是为了标新立异,完全没兴趣进一步了解,所以很快关闭了网页。

这次当我认真看了网站上的大部分内容,包括一些论文以及设计的 Demo,结合他自己写的简介,我终于理解了这个网站的信息架构。Bret 的个人网站其实是他自己写的一本书,这本书就是他的人生,章节是人生中的不同阶段。甚至,在他的网站中发现了很多年前给自己设计的一个仆告

在简介的左侧,介绍了他曾经探索的两个方向。一个方向是对「技术」的探索。正如他所说:

But technology has no soul, and code no conscience.

技术没有灵魂,代码没有意识,他认为在这条路上行走的人,仅仅是对代码的狂热。所以,他认为这条路是错误的。

另一个方向,是对「设计」这条路的探索,比如本文开头提到的那个演讲。经过一些实践和思考之后,他认为这条路也是错误的。他认为这条路只能教给人类回答问题。

最后,他希望能够找到一条路,里边「没有」技术,「没有」设计,只有人类自己的愿景,以及通过实现这个愿景的自驱动力。所以,他在 2014 年,开启了人生的第四篇章——dynamicland

在他的网站上,看到曾经推荐的一些资料,其中有一本书,名字叫《一个数学家的叹息》,在这本书的开篇有一句引言,我认为可以作为 Bret 探索方向的一个比喻:

如果你要造船,不要招揽人来搬木材,不要给人指派任务和工作,而是要教他们去渴望那广袤的大海。—— 安托万·得·圣埃克苏佩里

Bret 的网站上有很多有趣的资料,包括一些高质量的论文以及一些有趣的设计。对我的启发很大,帮助我跳出了「技术男」的思维框架。也让我明白了,「UI 设计」到底意味着什么?

不过,虽然我明白了前方道路意味着什么,但想要抵达前方,还是要一步一个脚印的走。但好在,我不会在迷茫中前行,也不会对前方的「景色」有不切实际的幻想。

感谢 Bret 将他的想法分享出来,也希望他的探索在未来能够有新的发现。

  1. Bret Victor 网站首页于 2024 年 3 月 1 日改版。之前的网站首页链接变成了 https://worrydream.com/Home2011/

修复完过往的 md 文件中的链接

断断续续将过往 Markdown 文件中的链接修复完成了。当前导入到 WordPress 的所有 Markdown 文件内容中的链接理论上都可以正常点击了。或许会因为部分链接的失效,无法打开页面,但至少不会出现 Markdonwn 的链接文本。

修复过程中粗略的浏览了部分过往的文章,感慨颇多。或许 2012 ~ 2014 年是从业以来最自由的几年,我不敢说以后的生活还能有这样的自由度,但目前来看基本上是一种奢望,不过倒是可以当做未来努力的方向。

2012 ~ 2014 这三年尝试了很多事情,但最终还是以成为独立开发者失败而告终。回头来看,也是必然的结果。那三年最大的收获就是做了不同的尝试,技术方向也转到互联网。另外不可忽略的一点,就是锻炼身体。

有机会以后可以总结一下那三年的得失。

互联网的默认设置是「开放」

前些天 Bluesky 开放注册了。Fesiverse 网络上掀起了一股要不要与 Bluesky 互相通信的讨论。

Fediverse 上的一部分「原住民」嫌弃 Bluesky 上的氛围,各种瞧不上,生怕污染了 Fediverse 上的良好氛围。然而 Fediverse 上的一些倡导者,则主张包容 Bluesky 的服务,提供可以互通或 block 的选择。

期间也看到一些理性的声音。认为互联网原本就是开放的,没道理互相 block。我当然是赞同这个观点。互联网从诞生那一天起就是默认开放的,如果担心隐私,就不要把数据放到互联网上。

不能因为担心自己发表的言论被别人看到就把别人的眼都蒙上,不能因为担心别人讲的话自己不爱听就把别人的嘴都堵上。可以制定规则,违反规则接受惩罚,但不能把大门一关,自成一统。

Bluesky 开放注册了

几天前,Bluesky 开放注册了。

我大概是前两个月申请的邀请码。当时注册账户之后只是关联了 micro.blog,上边发的信息几乎都是同步的 micro.blog 上的文章, 基本上没有特地上去单独发过信息。

相比 Bluesky 网络,反而 Fediverse 关注得更多一些。

对于两者的区别,从使用者角度来说,客户端方面我更喜欢 Ivory。内容上来说,我没啥发言权,毕竟 Bluesky 上的信息看得少。技术上来说,还没有深入研究过,据说是同宗,希望以后能和 Fediverse 融合。

另外,目前想找一款社交类聚合 App 好像还没有,特别是跟 Web 结合在一起的发布工具。

重新设计了一下主站

花了将近一下午的时间,重新「设计」了一下主域名的页面,主打一个简洁及可扩展性。

至此,整个网站的规划基本上差不多了,省下的就是往不同的子域名站点里填东西了。英文内容,现在主要集中在 Fediverse 上,在 IndieWeb 上注册了账号,主要发布英文内容,Mastodon 上的账号,就留着写中文内容吧,毕竟上边已经写了很多中文内容。另外,Mastodon 上的账号就留着自己用,就不在公共媒介透出了。

主域名的页面就是一个个人简介的集合站点,以后做了 App 或 Web 产品,就用子域名的方式做产品划分,全部规在 OhCoder.com 主域名下。这样既达到了可扩展性,又不会对已有的网站页面产生干扰,完全符合设计原则里的开放封闭原则。

如何看待钱

在这个竞争激烈且物欲横流的年代,「钱」是一个很敏感的话题。

自认为很幸运,大概十多年前就对钱有了一个比较理性且认为比较适合自己的观念,并且一直实践于生活中。

简单来说,钱对我来说就是一种资源,是真的当资源看,不是为了怕人议论,或者装什么清高,或为了政治正确(其实还是怕人背后议论),停留在口头层面上的那种。

举个例子,就像是饿了要吃饭,渴了要喝水的这种资源,只满足于功能性就可以,而不是在吃饱了还在想要更多的美食,已经解渴了还在想要更多水资源。

日常如果不买东西,身无分文我是没有任何问题的。但是如果需要日常开销,只要能满足支出需求,对我来说就足够了。我完全不需要原本只需花十块钱,但兜里一定要装一百块。

钱这种东西,只有在交易的时候,才能体现价值,才能体现其功能性。否则,存再多,也只是数字而已。

当然了,有的人对生活要求「高」,物质欲无穷无尽,需要无穷无尽的钱,我觉得这也没什么问题,那你就想办法挣钱就好了。挣到了,你享受你的生活,没挣到,你忍受你的生活。跟我也没什么关系,不矛盾。

钱对我来说,还有一种功能,就是可以为我提供安全感。至少有了钱我不会担心饿死,不会担心露宿街头。这肯定是必须的。所以我也会努力挣钱,这也无可厚非,不矛盾。但我知道,「地有多大产,人有多大胆」。我努力增加「产能」,为的也只是老了之后以备不时之需,但绝不是为了原本只需要花十块钱,兜里必须装一百块,甚至时不时在大庭广众之下拿出来用手点一点。

终归我对物质追求并没有那么高的要求,至少目前不是。这不是什么假清高,就是这样。就像有人追求奢靡的物质生活一样,我也不觉得有什么问题。能追到,你享受你的生活,追不到,你忍受你的生活。这完全没什么问题。

钱,在我这,永远都是一种资源。只有我控制它,没有它控制我。想都别想。

经济独立

想聊两句经济独立这件事。

我一直认为经济独立是一个挺容易理解的概念。但隐约感觉好像我理解的经济独立跟社会上的很多人理解的不太一样。

我自己理解的经济独立很简单,就是自己的收入能够支撑自己的支出,且有一定盈余,就算是经济独立。

比如我月收入十块钱,每月花五、六块钱就能满足我的日常支出,如果想买什么「大件」,存个一两个月就有能力购买,我认为这就是经济独立的。

而隔壁张三月收入一百块钱,支出两百块,银行借贷一百块,还不能满足开支,每月还得找人借钱。我认为这就是没有经济独立。即便他挣得可能比我多很多。

只要保持经济独立,就可以保证自己的人格独立,尊严独立。在我眼里,根本不存在月收入十块钱就一定要去谄媚或者心虚月收入一百块钱的,甚至更没必要觉得自己低人一等。

你有你的一百块钱的活法,我有我的十块钱的活法。

我是绝对不会因为自己的收入比别人少就觉得自己低人一等。如果有人因为自己有钱跑到我面前装逼。礼貌的做法是,不鸟你就完事了。自我主动刷存在感没完没了的,直接拉黑,这辈子不见。

社会上有大量的人因为自己收入少而嫉妒收入多的人。这些人大多是虚荣心太强,自己的欲望没办法通过与自己能力匹配的收入来实现,想通过抱有钱人的大腿来满足自己的虚荣心,所以为了有求于他人而不得不让自己变得低三下四。

我是绝对不会同情这类人。相反,我倒是觉得这挺公平的,你为了满足自己的欲望,就得付出代价,抱有钱人大腿也是代价的一部分。而且,更让人不耻的是,这些人报完大腿反过头来就骂大腿的娘。

完。