SwiftUI:Custom Button

SwiftUI:可組合的視圖中,我們已經(jīng)介紹了如何做一個良好的UI設(shè)計独悴。
在本文中例书,我們看下新組件Button該如何設(shè)計:

button.png

開始

為了方便,我們創(chuàng)建一個新的ButtonStyle:

struct FSButton: View {
  let titleKey: LocalizedStringKey
  let action: () -> Void

  init(_ titleKey: LocalizedStringKey, action: @escaping () -> Void) {
    self.titleKey = titleKey
    self.action = action
  }

  var body: some View {
    Button(action: action, label: { Text(titleKey).bold() })
      .buttonStyle(FSButtonStyle())
  }
}

private struct FSButtonStyle: ButtonStyle {
  func makeBody(configuration: Configuration) -> some View {
    HStack {
      Spacer()
      configuration.label
        .foregroundColor(.white)
      Spacer()
    }
    .padding()
    .background(
      RoundedRectangle(cornerRadius: 8, style: .continuous).fill(Color.green)
    )
    .opacity(configuration.isPressed ? 0.5 : 1)
  }
}

我們可以這么使用:

FSButton("Button title") {
  // button tap action
}
button1.gif

這是我們的預(yù)覽效果刻炒,我們可以把它移植到項目中去了决采。

一周后

一周過去了,產(chǎn)品告訴我們坟奥,有些按鈕文本將由我們的后端提供树瞭,而不是由應(yīng)用程序處理拇厢。
為了使用我們當前的API,我們需要顯式地將后端字符串轉(zhuǎn)換為LocalizedStringKey:

var backendString: String

...

FSButton(LocalizedStringKey(backendString), action: buttonDidTap)

然而這并不理想:

  • 我們?yōu)E用LocalizedStringKey
  • 我們會觸發(fā)一個運行時查找一個我們已經(jīng)知道不會在我們的Localizable.string表中

處理這個問題的一種更好的方法是使用Text的初始化方法init<S: StringProtocol>(_ content: S)晒喷,其目的是顯示字符串的原樣孝偎,而不首先嘗試對它們進行本地化。

我們需要一種方法來同時兼容這個新的初始化方法和之前的初始化方法凉敲。一種解決方法就是用一個title Text替換FSButtontitleKey LocalizedStringKey屬性:

struct FSButton: View {
  let title: Text
  let action: () -> Void

  init(titleKey: LocalizedStringKey, action: @escaping () -> Void) {
    self.title = Text(titleKey)
    self.action = action
  }

  init<S: StringProtocol>(_ content: S, action: @escaping () -> Void) {
    self.title = Text(content)
    self.action = action
  }

  var body: some View {
    Button(action: action, label: { title.bold() })
      .buttonStyle(FSButtonStyle())
  }
}

使用這種方式衣盾,FSButton將動態(tài)地使用適當?shù)某跏蓟椒▌?chuàng)建文本實例:

FSButton(titleKey: "my_localized_title", action: { ... })

var backendString: String = ...
FSButton(backendString, action: { ... })
vstack.png

你現(xiàn)在知道怎么用泰語寫“Button title”了!

不久之后

營銷團隊已經(jīng)聽說了按鈕的文本后端驅(qū)動方法,現(xiàn)在他們希望我們能夠讓他們也能驅(qū)動這樣的設(shè)計:

marketing.png

這在我們的兩個初始化方法中是不可能實現(xiàn)的爷抓。然而势决,Text是最靈活和動態(tài)的SwiftUI視圖之一,它有一整套專門的修飾符來返回其他Text文本視圖,甚至可以向其他Text文本視圖添加Text文本視圖,結(jié)果仍然是另一個Text文本:

let text: Text =
  Text("Default ") +
  Text("italic ").italic() +
  Text("Big ").font(.title) +
  Text("Red ").foregroundColor(.red) +
  Text("underline").underline()

因此废赞,為了支持這個新需求,從FSButton的角度來看叮姑,我們需要做的就是公開一個新的初始化方法接受一個Text:

extension FSButton {
  init(_ title: Text, action: @escaping () -> Void) {
    self.title = title
    self.action = action
  }
}

這使得創(chuàng)建視圖成為可能唉地,例如

complex.png

有了這個初始化器,我們可以完全覆蓋按鈕文本的默認樣式传透,使用不同的字體耘沼、粗細、文本顏色等朱盐,從而展示各種新的按鈕樣式群嗤。

結(jié)束

最終代碼如下:

import SwiftUI

struct ContentView: View {
  var backendString: String = "some backend string"

  var body: some View {
    let marketingText: Text =
      Text("Please please ").italic() +
      Text("tap me ") +
      Text("NOW!").underline().bold().font(.title)

    let exampleText: Text =
      Text("Default ") +
      Text("italic ").italic() +
      Text("Big ").font(.title) +
      Text("Red ").foregroundColor(.red) +
      Text("underline").underline()

    VStack {
      FSButton(titleKey: "my_localized_title") {}
      FSButton(backendString) {}
      FSButton(marketingText) {}
      FSButton(exampleText) {}
    }
    .padding()
  }
}

struct ContentView_Previews: PreviewProvider {
  static var previews: some View {
    ContentView()
      .padding()
      .previewLayout(.sizeThatFits)
      .environment(\.locale, .init(identifier: "th"))
  }
}

自定義FSButton:

struct FSButton: View {
  let title: Text
  let action: () -> Void

  init(_ title: Text, action: @escaping () -> Void) {
    self.title = title
    self.action = action
  }

  init<S: StringProtocol>(_ content: S, action: @escaping () -> Void) {
    self.title = Text(content)
    self.action = action
  }

  init(titleKey: LocalizedStringKey, tableName: String? = nil, bundle: Bundle? = nil, comment: StaticString? = nil, action: @escaping () -> Void) {
    self.title = Text(titleKey, tableName: tableName, bundle: bundle, comment: comment)
    self.action = action
  }

  var body: some View {
    Button(action: action, label: { title.bold() })
      .buttonStyle(FSButtonStyle())
  }
}

private struct FSButtonStyle: ButtonStyle {
  func makeBody(configuration: Configuration) -> some View {
    HStack {
      Spacer()
      configuration.label
        .foregroundColor(.white)
      Spacer()
    }
    .padding()
    .background(
      RoundedRectangle(cornerRadius: 8, style: .continuous).fill(Color.green)
    )
    .opacity(configuration.isPressed ? 0.5 : 1)
  }
}

總結(jié)

構(gòu)建顯示文本的SwiftUI組件可能比預(yù)期的要復(fù)雜,然而兵琳,我們這邊只需很少的工作就可以覆蓋大多數(shù)用例狂秘。
我們在這里看到的同樣的方法和思考過程也被應(yīng)用到許多swiftUI視圖定義中,不同的視圖會暴露不同的初始化方法躯肌。

如果你正在尋找類似方案者春,以下是SwiftUI使用這種方法的一些思路:Button, ColorPicker, CommandMenu, DatePicker, DisclosureGroup, Label, Link, Menu, NavigationLink, Picker, ProgressView, SecureField, Stepper, TextField, Toggle, WindowGroup。還有一些modifiers修飾詞清女,如navigationTitle钱烟。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市嫡丙,隨后出現(xiàn)的幾起案子拴袭,更是在濱河造成了極大的恐慌,老刑警劉巖曙博,帶你破解...
    沈念sama閱讀 210,978評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件拥刻,死亡現(xiàn)場離奇詭異,居然都是意外死亡父泳,警方通過查閱死者的電腦和手機泰佳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評論 2 384
  • 文/潘曉璐 我一進店門盼砍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事〔杼唬” “怎么了波岛?”我有些...
    開封第一講書人閱讀 156,623評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長娘荡。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么觉渴? 我笑而不...
    開封第一講書人閱讀 56,324評論 1 282
  • 正文 為了忘掉前任,我火速辦了婚禮徽惋,結(jié)果婚禮上案淋,老公的妹妹穿的比我還像新娘。我一直安慰自己险绘,他們只是感情好踢京,可當我...
    茶點故事閱讀 65,390評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著宦棺,像睡著了一般瓣距。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上代咸,一...
    開封第一講書人閱讀 49,741評論 1 289
  • 那天蹈丸,我揣著相機與錄音,去河邊找鬼呐芥。 笑死逻杖,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的思瘟。 我是一名探鬼主播弧腥,決...
    沈念sama閱讀 38,892評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼潮太!你這毒婦竟也來了管搪?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,655評論 0 266
  • 序言:老撾萬榮一對情侶失蹤铡买,失蹤者是張志新(化名)和其女友劉穎更鲁,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體奇钞,經(jīng)...
    沈念sama閱讀 44,104評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡澡为,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了景埃。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片媒至。...
    茶點故事閱讀 38,569評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡顶别,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出拒啰,到底是詐尸還是另有隱情驯绎,我是刑警寧澤,帶...
    沈念sama閱讀 34,254評論 4 328
  • 正文 年R本政府宣布谋旦,位于F島的核電站剩失,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏册着。R本人自食惡果不足惜拴孤,卻給世界環(huán)境...
    茶點故事閱讀 39,834評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望甲捏。 院中可真熱鬧演熟,春花似錦、人聲如沸司顿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽免猾。三九已至是辕,卻和暖如春囤热,著一層夾襖步出監(jiān)牢的瞬間猎提,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評論 1 264
  • 我被黑心中介騙來泰國打工旁蔼, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留锨苏,地道東北人。 一個月前我還...
    沈念sama閱讀 46,260評論 2 360
  • 正文 我出身青樓棺聊,卻偏偏與公主長得像伞租,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子限佩,可洞房花燭夜當晚...
    茶點故事閱讀 43,446評論 2 348

推薦閱讀更多精彩內(nèi)容