Π’ΡƒΡ‚ΠΎΡ€ΠΈΠ°Π»Ρ‹
07 фСвраля 2023
Π’ΡƒΡ‚ΠΎΡ€ΠΈΠ°Π»Ρ‹
07 фСвраля 2023
Бтилизация ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ΠΎΠ² Π² SwiftUI

SwiftUI ΠΈΠΌΠ΅Π΅Ρ‚ ΠΎΡ‚Π»ΠΈΡ‡Π½Ρ‹ΠΉ API для стилизации view нСзависимо ΠΎΡ‚ ΠΈΡ… Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ. Π’ этом постС ΠΌΡ‹ рассмотрим, ΠΊΠ°ΠΊ ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΡΡ‚ΠΈΠ»ΠΈΠ·ΠΎΠ²Π°Ρ‚ΡŒ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΡΠΊΠΈΠ΅ view Ρ‚Π°ΠΊΠΈΠΌ ΠΆΠ΅ ΠΎΠ±Ρ€Π°Π·ΠΎΠΌ.

Π’ ΠΏΡ€ΠΎΡˆΠ»ΠΎΠΌ Π³ΠΎΠ΄Ρƒ Π² Ρ…ΠΎΠ΄Π΅ Π½Π΅ΡΠΊΠΎΠ»ΡŒΠΊΠΈΡ… эпизодов Π½Π° Swift Talk ΠΌΡ‹ продСмонстрировали, ΠΊΠ°ΠΊ ΡΠΎΠ·Π΄Π°Ρ‚ΡŒ собствСнный стСппСр для увСличСния ΠΈ ΡƒΠΌΠ΅Π½ΡŒΡˆΠ΅Π½ΠΈΡ значСния. Он Π±Ρ‹Π» ΠΏΠΎΡ…ΠΎΠΆ Π½Π° Stepper Π² SwiftUI, Π½ΠΎ с API, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ Π΄Π΅Π»Π°Π΅Ρ‚ Π΅Π³ΠΎ ΡΡ‚ΠΈΠ»ΡŒΠ½Ρ‹ΠΌ.

Π­Ρ‚ΠΎΡ‚ пост являСтся ΠΊΡ€Π°Ρ‚ΠΊΠΈΠΌ ΠΈΠ·Π»ΠΎΠΆΠ΅Π½ΠΈΠ΅ΠΌ Ρ‚ΠΎΠ³ΠΎ, Ρ‡Ρ‚ΠΎ ΠΌΡ‹ рассмотрСли Ρ‚ΠΎΠ³Π΄Π°, Π° Ρ‚Π°ΠΊΠΆΠ΅ нСсколькими ΠΏΡ€ΠΈΡ‘ΠΌΠ°ΠΌΠΈ, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΌ ΠΌΡ‹ Π½Π°ΡƒΡ‡ΠΈΠ»ΠΈΡΡŒ с Ρ‚Π΅Ρ… ΠΏΠΎΡ€, Ρ‡Ρ‚ΠΎΠ±Ρ‹ наши ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΡΠΊΠΈΠ΅ стили view (прСдставлСниС, вью, вьюшка) Ρ€Π°Π±ΠΎΡ‚Π°Π»ΠΈ Π΅Ρ‰Ρ‘ Π»ΡƒΡ‡ΡˆΠ΅, ΠΊΠ°ΠΊ built-in (встроСнныС) Π² SwiftUI. Π’ ΠΏΠΎΡΠ»Π΅Π΄ΡƒΡŽΡ‰Π΅ΠΌ постС ΠΌΡ‹ рассмотрим нСсколько Π±ΠΎΠ»Π΅Π΅ ΠΏΡ€ΠΎΠ΄Π²ΠΈΠ½ΡƒΡ‚Ρ‹Ρ… Π²Π°Ρ€ΠΈΠ°Π½Ρ‚ΠΎΠ² использования.

Β 

Π‘Ρ‚ΠΈΠ»ΡŒ ΠΊΠ½ΠΎΠΏΠΊΠΈ

Для Π½Π°Ρ‡Π°Π»Π° Π΄Π°Π²Π°ΠΉΡ‚Π΅ посмотрим Π½Π° ΠΏΡ€ΠΎΡΡ‚ΡƒΡŽ ΠΊΠ½ΠΎΠΏΠΊΡƒ:

ΠžΠ±Ρ‹Ρ‡Π½Π°Ρ ΠΊΠ½ΠΎΠΏΠΊΠ° SwiftUI с синСй надписью Β«ΠŸΡ€ΠΎΠ΄ΠΎΠ»ΠΆΠΈΡ‚ΡŒΒ».

Button("Continue") {
Β  Β  // Continue
}

Β 
ΠœΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΠΈΠ·ΠΌΠ΅Π½ΠΈΡ‚ΡŒ ΡΡ‚ΠΈΠ»ΡŒ этой ΠΊΠ½ΠΎΠΏΠΊΠΈ, Π΄ΠΎΠ±Π°Π²ΠΈΠ² нСсколько ΠΌΠΎΠ΄ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ΠΎΠ² view ΠΈ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡ Π΄Ρ€ΡƒΠ³ΠΎΠΉ ΠΌΠ΅Ρ‚ΠΎΠ΄ для создания label (ΠΌΠ΅Ρ‚ΠΊΠΈ) ΠΊΠ½ΠΎΠΏΠΊΠΈ:

Button { Β  Β 
Β  Β  // Continue
} label: {
Β  Β  HStack {
Β  Β  Β  Β  Spacer()
Β  Β  Β  Β  Text("Continue")
Β  Β  Β  Β  Spacer()
Β  Β  }
Β  Β  .padding(EdgeInsets(top: 12, leading: 24, bottom: 12, trailing: 24))
Β  Β  }
Β  Β  .font(.system(.title2, design: .rounded, weight: .bold))
Β  Β  .foregroundColor(.yellow)
Β  Β  .background(Capsule().stroke(.yellow, lineWidth: 2))

Β 

Π₯отя SwiftUI Π΄Π΅Π»Π°Π΅Ρ‚ настройку view ΠΎΡ‡Π΅Π½ΡŒ ΡƒΠ΄ΠΎΠ±Π½ΠΎΠΉ, Π½ΠΎ ΠΏΠ»ΠΎΡ…ΠΎ ΠΌΠ°ΡΡˆΡ‚Π°Π±ΠΈΡ€ΡƒΠ΅Ρ‚ΡΡ. НСобходимо ΠΊΠ°ΠΆΠ΄Ρ‹ΠΉ Ρ€Π°Π· ΠΏΡ€ΠΈΠΌΠ΅Π½ΡΡ‚ΡŒ ΠΎΠ΄Π½ΠΈ ΠΈ Ρ‚Π΅ ΠΆΠ΅ ΠΌΠΎΠ΄ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€Ρ‹ ΠΈ ΠΎΠ±ΠΎΡ€Π°Ρ‡ΠΈΠ²Π°Ρ‚ΡŒ ΠΌΠ΅Ρ‚ΠΊΡƒ Π² HStack. Π’Π°ΠΊΠΈΠΌ ΠΎΠ±Ρ€Π°Π·ΠΎΠΌ, Π½Π°ΠΌ Π½ΡƒΠΆΠ΅Π½ Π±ΠΎΠ»Π΅Π΅ ΠΌΠ½ΠΎΠ³ΠΎΡ€Π°Π·ΠΎΠ²Ρ‹ΠΉ ΠΏΠΎΠ΄Ρ…ΠΎΠ΄.

Кнопка с ΠΆΠ΅Π»Ρ‚Ρ‹ΠΌ ΠΊΠΎΠ½Ρ‚ΡƒΡ€ΠΎΠΌ Π² Ρ„ΠΎΡ€ΠΌΠ΅ капсулы.  Он ΠΈΠΌΠ΅Π΅Ρ‚ ΠΌΠ΅Ρ‚ΠΊΡƒ Β«ΠŸΡ€ΠΎΠ΄ΠΎΠ»ΠΆΠΈΡ‚ΡŒΒ» ΠΈ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ Π·Π°ΠΊΡ€ΡƒΠ³Π»Π΅Π½Π½Ρ‹ΠΉ ΡˆΡ€ΠΈΡ„Ρ‚.
Вакая ΠΊΠ½ΠΎΠΏΠΊΠ° Π²ΠΎ всю ΡˆΠΈΡ€ΠΈΠ½Ρƒ являСтся распространённым стилСм, Π½ΠΎ Ρ€Π΅Π°Π»ΠΈΠ·ΠΎΠ²Π°Ρ‚ΡŒ Π΅Ρ‘ нСпросто.

Β 

ΠŸΠΎΠ²Ρ‚ΠΎΡ€Π½ΠΎΠ΅ использованиС стиля

ΠŸΡ€ΠΈ создании прилоТСния Π½Π°ΠΌ ΠΊΠ°ΠΊ ΠΏΡ€Π°Π²ΠΈΠ»ΠΎ Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ, Ρ‡Ρ‚ΠΎΠ±Ρ‹ view ΠΈ элСмСнты управлСния ΠΈΠΌΠ΅Π»ΠΈ Π΅Π΄ΠΈΠ½Ρ‹ΠΉ ΡΡ‚ΠΈΠ»ΡŒ Π²ΠΎ всСм ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠΈ, ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ ΠΈΡ… ΡƒΠ·Π½Π°Π²Π°Π΅ΠΌΡ‹ΠΌΠΈ для ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ ΠΏΡ€ΠΈ ΠΏΠ΅Ρ€Π΅Ρ…ΠΎΠ΄Π΅ с экрана Π½Π° экран, Π° Ρ‚Π°ΠΊΠΆΠ΅ ΡƒΡΡ‚Π°Π½ΠΎΠ²ΠΈΡ‚ΡŒ Ρ‚Π΅ΠΌΡƒ для прилоТСния ΠΈΠ»ΠΈ привязку ΠΊ Π±Ρ€Π΅Π½Π΄Ρƒ ΠΊΠΎΠΌΠΏΠ°Π½ΠΈΠΈ.

Π§Ρ‚ΠΎΠ±Ρ‹ ΡƒΠΏΡ€ΠΎΡΡ‚ΠΈΡ‚ΡŒ ΠΏΡ€ΠΈΠΌΠ΅Π½Π΅Π½ΠΈΠ΅ ΠΎΠ΄Π½ΠΎΠ³ΠΎ ΠΈ Ρ‚ΠΎΠ³ΠΎ ΠΆΠ΅ стиля ΠΊΠΎ ΠΌΠ½ΠΎΠ³ΠΈΠΌ view Button, ΠΎΠ΄ΠΈΠ½ ΠΈΠ· Π²Π°Ρ€ΠΈΠ°Π½Ρ‚ΠΎΠ² β€” это ΡΠΎΠ·Π΄Π°Ρ‚ΡŒ Π½ΠΎΠ²ΠΎΠ΅ view ΠΊΠ½ΠΎΠΏΠΊΠΈ с API, Π°Π½Π°Π»ΠΎΠ³ΠΈΡ‡Π½Ρ‹ΠΌ SwiftUI Button, ΠΈ ΠΏΡ€ΠΈΠΌΠ΅Π½ΠΈΡ‚ΡŒ ΠΊ Π½Π΅ΠΌΡƒ ΡΡ‚ΠΈΠ»ΡŒ:

struct MyButton: View {
Β  Β  var action: () -> Void
Β  Β  var label: Label

Β  Β  init(action: @escaping () -> Void, @ViewBuilder label: () -> Label) {
Β  Β  Β  Β  self.action = action
Β  Β  Β  Β  self.label = label()
Β  Β  }

Β  Β  var body: some View {
Β  Β  Β  Β  Button {
Β  Β  Β  Β  Β  Β  action()
Β  Β  Β  Β  } label: {
Β  Β  Β  Β  Β  Β  HStack {
Β  Β  Β  Β  Β  Β  Β  Β  Spacer()
Β  Β  Β  Β  Β  Β  Β  Β  Label
Β  Β  Β  Β  Β  Β  Β  Β  Spacer()
Β  Β  Β  Β  Β  Β  }
Β  Β  Β  Β  Β  Β  .padding(EdgeInsets(top: 12, leading: 24, bottom: 12, trailing: 24))
Β  Β  Β  Β  }
Β  Β  Β  Β  .font(.system(.title2, design: .rounded, weight: .bold))
Β  Β  Β  Β  .foregroundColor(.yellow)
Β  Β  Β  Β  .background(Capsule().stroke(.yellow, lineWidth: 2))
Β  Β  }
}

Β 

Однако созданиС ΠΎΠ±Ρ‘Ρ€Ρ‚ΠΊΠΈ view, ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΈΠ²Π°ΡŽΡ‰Π΅Π³ΠΎ Ρ‚Π΅ ΠΆΠ΅ ΡƒΠ΄ΠΎΠ±Π½Ρ‹Π΅ ΠΈΠ½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·Π°Ρ‚ΠΎΡ€Ρ‹, Ρ‡Ρ‚ΠΎ ΠΈ ΠΎΠ±Ρ‘Ρ€Ρ‚ΠΊΠ° view, ΠΌΠΎΠΆΠ΅Ρ‚ ΠΏΠΎΡ‚Ρ€Π΅Π±ΠΎΠ²Π°Ρ‚ΡŒ Π½Π΅ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΉ Ρ€Π°Π±ΠΎΡ‚Ρ‹, ΠΊΠ°ΠΊ ΠΏΠΎΠΊΠ°Π·Π°Π½ΠΎ Π² ΠΏΡ€ΠΈΠ²Π΅Π΄Ρ‘Π½Π½ΠΎΠΌ Π½ΠΈΠΆΠ΅ ΠΊΠΎΠ΄Π΅:

struct MyButton where Label == Text {
Β  Β  @_disfavoredOverload
Β  Β  init(_ title: some StringProtocol, role: ButtonRole? = nil, action: @escaping () -> Void) {
Β  Β  Β  Β  self.action = action
Β  Β  Β  Β  self.role = role
Β  Β  Β  Β  self.label = Text(title)
Β  Β  }
Β  Β  init(_ titleKey: LocalizedStringKey, role: ButtonRole? = nil, action: @escaping () -> Void) {
Β  Β  Β  Β  self.action = action
Β  Β  Β  Β  self.role = role
Β  Β  Β  Β  self.label = Text(titleKey)
Β  Β  }
}


НапримСр, ΠΊΠ°ΠΊ ΠΏΠΎΠΊΠ°Π·Π°Π½ΠΎ Π² этом ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π΅ ΠΊΠΎΠ΄Π°, Button ΠΎΠ±Ρ€Π°Π±Π°Ρ‚Ρ‹Π²Π°Π΅Ρ‚ Π»ΠΈΡ‚Π΅Ρ€Π°Π» String ΠΊΠ°ΠΊ LocalizedStringKey, ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡ шаблон @_disfavoredOverload. ΠŸΠΎΠ΄ΠΎΠ±Π½Ρ‹Π΅ тонкости ΠΌΠΎΠ³ΡƒΡ‚ ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ написаниС встроСнной Π·Π°ΠΌΠ΅Π½Ρ‹ встроСнного view Π±ΠΎΠ»Π΅Π΅ Ρ‚Ρ€ΡƒΠ΄ΠΎΡ‘ΠΌΠΊΠΎΠΉ, Ρ‡Π΅ΠΌ оТидалось.

Π₯отя ΡΡ‚ΠΈΠ»ΡŒ, ΠΎΠΏΡ€Π΅Π΄Π΅Π»Ρ‘Π½Π½Ρ‹ΠΉ Π² ΠΎΠ΄Π½ΠΎΠΌ мСстС, это Ρ…ΠΎΡ€ΠΎΡˆΠΎ, Π²Π°ΠΆΠ½ΠΎ Π½Π΅ Π·Π°Π±Ρ‹Π²Π°Ρ‚ΡŒ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ view MyButton вмСсто ΠΊΠ½ΠΎΠΏΠΊΠΈ SwiftUI Π²ΠΎ всём ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠΈ. Π’ ΠΏΡ€ΠΎΡ‚ΠΈΠ²Π½ΠΎΠΌ случаС ΠΌΡ‹ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΠΌ нСсогласованный ΡΡ‚ΠΈΠ»ΡŒ.

VStack {
Β  Β  MyButton("OK") {
Β  Β  Β  Β  // Confirm
Β  Β  }
Β  Β  Button("Cancel") {
Β  Β  Β  Β  // Oops, wrong button
Β  Β  }
}

Π”Π²Π΅ ΠΊΠ½ΠΎΠΏΠΊΠΈ: ΠΎΠ΄Π½Π° с ΠΆΠ΅Π»Ρ‚Ρ‹ΠΌ ΠΊΠΎΠ½Ρ‚ΡƒΡ€ΠΎΠΌ Π² Ρ„ΠΎΡ€ΠΌΠ΅ капсулы, с ΠΊΡ€ΡƒΠΏΠ½Ρ‹ΠΌ Π·Π°ΠΊΡ€ΡƒΠ³Π»Π΅Π½Π½Ρ‹ΠΌ ΡˆΡ€ΠΈΡ„Ρ‚ΠΎΠΌ ΠΈ Π²ΠΎ всю ΡˆΠΈΡ€ΠΈΠ½Ρƒ, Π° другая Ρ‚ΠΎΠ»ΡŒΠΊΠΎ с синСй ΠΌΠ΅Ρ‚ΠΊΠΎΠΉ.

К ΡΡ‡Π°ΡΡ‚ΡŒΡŽ, Ρƒ SwiftUI Π΅ΡΡ‚ΡŒ API, Ρ€Π΅ΡˆΠ°ΡŽΡ‰ΠΈΠΉ эту ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΡƒ.

Β 

View стилСй

API view стилСй SwiftUI Ρ€Π°Π±ΠΎΡ‚Π°ΡŽΡ‚ Ρ‚Π°ΠΊ ΠΆΠ΅, ΠΊΠ°ΠΊ ΠΌΠΎΠ΄ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€Ρ‹ view font(_:) ΠΈ tint(_:), ΠΏΠΎΡΠΊΠΎΠ»ΡŒΠΊΡƒ ΠΎΠ½ΠΈ ΠΏΠΎΠ·Π²ΠΎΠ»ΡΡŽΡ‚ Π΄ΠΎΠ±Π°Π²Π»ΡΡ‚ΡŒ ΡΡ‚ΠΈΠ»ΡŒ Π² ΠΈΠ΅Ρ€Π°Ρ€Ρ…ΠΈΡŽ view ΠΈ ΠΏΡ€ΠΈΠΌΠ΅Π½ΡΡ‚ΡŒ этот ΡΡ‚ΠΈΠ»ΡŒ ΠΊΠΎ всСм ΡΠΎΠΎΡ‚Π²Π΅Ρ‚ΡΡ‚Π²ΡƒΡŽΡ‰ΠΈΠΌ view Π² этой ΠΈΠ΅Ρ€Π°Ρ€Ρ…ΠΈΠΈ:

HStack {
Β  Β  Button("Undo") { /* … */ }
Β  Β  Button("Redo") { /* … */ }
}
.foregroundColor(.black)
.font(.system(.title3, design: .rounded).bold())
.tint(.yellow)
.buttonStyle(.borderedProminent)

Π”Π²Π΅ ΠΆΠ΅Π»Ρ‚Ρ‹Π΅ ΠΊΠ½ΠΎΠΏΠΊΠΈ Π² HStack с Π·Π°ΠΊΡ€ΡƒΠ³Π»Π΅Π½Π½Ρ‹ΠΌ ΡˆΡ€ΠΈΡ„Ρ‚ΠΎΠΌ ΠΈ Π²Ρ‹Π΄Π΅Π»Π΅Π½Π½Ρ‹ΠΌ стилСм с ΠΎΠΊΠ°Π½Ρ‚ΠΎΠ²ΠΊΠΎΠΉ.
Β 
Π—Π΄Π΅ΡΡŒ Ρ†Π²Π΅Ρ‚ ΠΏΠ΅Ρ€Π΅Π΄Π½Π΅Π³ΠΎ ΠΏΠ»Π°Π½Π°, ΡˆΡ€ΠΈΡ„Ρ‚, ΠΎΡ‚Ρ‚Π΅Π½ΠΎΠΊ ΠΈ ΡΡ‚ΠΈΠ»ΡŒ ΠΊΠ½ΠΎΠΏΠΊΠΈ, ΡƒΠΊΠ°Π·Π°Π½Π½Ρ‹Π΅ Π² HStack, Ρ€Π°ΡΠΏΡ€ΠΎΡΡ‚Ρ€Π°Π½ΡΡŽΡ‚ΡΡ Π½Π° ΠΊΠ°ΠΆΠ΄ΡƒΡŽ ΠΊΠ½ΠΎΠΏΠΊΡƒ.

ΠœΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ это ΠΏΠΎΠ²Π΅Π΄Π΅Π½ΠΈΠ΅, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΡƒΠ±Π΅Π΄ΠΈΡ‚ΡŒΡΡ, Ρ‡Ρ‚ΠΎ ΠΎΠ΄ΠΈΠ½ ΠΈ Ρ‚ΠΎΡ‚ ΠΆΠ΅ ΡΡ‚ΠΈΠ»ΡŒ ΠΊΠ½ΠΎΠΏΠΊΠΈ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ Π²ΠΎ всСм ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠΈ, примСняя ΠΌΠΎΠ΄ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ Π² ΠΊΠΎΡ€Π½Π΅ ΠΈΠ΅Ρ€Π°Ρ€Ρ…ΠΈΠΈ view:

@main
struct MyApp: App {
Β  Β  var body: some Scene {
Β  Β  Β  Β  WindowGroup {
Β  Β  Β  Β  Β  Β  ContentView()
Β  Β  Β  Β  Β  Β  Β  Β  .tint(.yellow)
Β  Β  Β  Β  Β  Β  Β  Β  .buttonStyle(.borderedProminent)
Β  Β  Β  Β  }
Β  Β  }
}

Β 

SwiftUI ΠΈΠΌΠ΅Π΅Ρ‚ нСсколько встроСнных стилСй Π½Π° Π²Ρ‹Π±ΠΎΡ€, Π° для Π½Π΅ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Ρ… view, Π΄ΠΎΠΏΡƒΡΠΊΠ°ΡŽΡ‰ΠΈΡ… стили, ΠΌΠΎΠΆΠ½ΠΎ ΡΠΎΠ·Π΄Π°Π²Π°Ρ‚ΡŒ Π½ΠΎΠ²Ρ‹Π΅ стили.

Β 

Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΡΠΊΠΎΠ³ΠΎ стиля ΠΊΠ½ΠΎΠΏΠΊΠΈ

Π§Ρ‚ΠΎΠ±Ρ‹ ΠΏΠΎΠ»Π½ΠΎΡΡ‚ΡŒΡŽ Π½Π°ΡΡ‚Ρ€ΠΎΠΈΡ‚ΡŒ ΡΡ‚ΠΈΠ»ΡŒ ΠΊΠ½ΠΎΠΏΠΊΠΈ, Π½Π°ΠΌ сначала Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ Ρ‡Ρ‚ΠΎ-Ρ‚ΠΎ, Ρ‡Ρ‚ΠΎ SwiftUI ΠΌΠΎΠΆΠ΅Ρ‚ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ для стилизации ΠΏΡ€ΠΈ ΠΎΡ‚ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΈ ΠΊΠ½ΠΎΠΏΠΊΠΈ.

ΠœΠΎΠ΄ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ buttonStyle(_:), ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ ΠΌΡ‹ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌ для установки стиля ΠΊΠ½ΠΎΠΏΠΎΠΊ, ΠΏΡ€ΠΈΠ½ΠΈΠΌΠ°Π΅Ρ‚ Ρ‚ΠΈΠΏ, ΡΠΎΠΎΡ‚Π²Π΅Ρ‚ΡΡ‚Π²ΡƒΡŽΡ‰ΠΈΠΉ Π»ΠΈΠ±ΠΎ ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ»Ρƒ ButtonStyle, Π»ΠΈΠ±ΠΎ ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ»Ρƒ PrimitiveButtonStyle.

Π˜Ρ‚Π°ΠΊ, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΡΠΎΠ·Π΄Π°Ρ‚ΡŒ собствСнный ΡΡ‚ΠΈΠ»ΡŒ, ΠΌΡ‹ создаСм Π½ΠΎΠ²Ρ‹ΠΉ Ρ‚ΠΈΠΏ, ΡΠΎΠΎΡ‚Π²Π΅Ρ‚ΡΡ‚Π²ΡƒΡŽΡ‰ΠΈΠΉ ΠΎΠ΄Π½ΠΎΠΌΡƒ ΠΈΠ· этих ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ»ΠΎΠ².

Для ΠΎΠ±ΠΎΠΈΡ… ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ»ΠΎΠ² трСбуСтся функция makeBody(configuration:). ΠšΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΡ, пСрСданная этой Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ, ΠΈΠΌΠ΅Π΅Ρ‚ нСсколько свойств, ΠΏΡ€Π΅Π΄ΡΡ‚Π°Π²Π»ΡΡŽΡ‰ΠΈΠ΅ ΠΊΠ½ΠΎΠΏΠΊΡƒ, ΠΊΠΎΡ‚ΠΎΡ€ΡƒΡŽ ΠΌΡ‹ оформляСм.

ΠœΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ configuration.label, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΡ‚ΡŒ view, ΠΏΡ€Π΅Π΄ΡΡ‚Π°Π²Π»ΡΡŽΡ‰Π΅Π΅ ΠΌΠ΅Ρ‚ΠΊΡƒ ΠΊΠ½ΠΎΠΏΠΊΠΈ, ΠΈ ΠΏΡ€ΠΈΠΌΠ΅Π½ΠΈΡ‚ΡŒ наш ΡΡ‚ΠΈΠ»ΡŒ ΠΊ этой ΠΌΠ΅Ρ‚ΠΊΠ΅:

struct CustomButtonStyle: ButtonStyle {
Β  Β  func makeBody(configuration: Configuration) -> some View {
Β  Β  Β  Β  HStack {
Β  Β  Β  Β  Β  Β  Spacer()
Β  Β  Β  Β  Β  Β  configuration.label
Β  Β  Β  Β  Β  Β  Spacer()
Β  Β  Β  Β  }
Β  Β  Β  Β  .padding(EdgeInsets(top: 12, leading: 24, bottom: 12, trailing: 24))
Β  Β  Β  Β  .font(.system(.title2, design: .rounded).bold())
Β  Β  Β  Β  .padding(EdgeInsets(top: 12, leading: 24, bottom: 12, trailing: 24))
Β  Β  Β  Β  .foregroundColor(.yellow)
Β  Β  Β  Β  .background {
Β  Β  Β  Β  Β  Β  Capsule()
Β  Β  Β  Β  Β  Β  Β  Β  .stroke(.yellow, lineWidth: 2)
Β  Β  Β  Β  }
Β  Β  }
}

Β 

ΠŸΡ€ΠΈΠ²Π΅Π΄Π΅Π½Π½Ρ‹ΠΉ Π²Ρ‹ΡˆΠ΅ ΠΊΠΎΠ΄ позволяСт Π½Π°ΠΌ ΡƒΡΡ‚Π°Π½ΠΎΠ²ΠΈΡ‚ΡŒ ΡΡ‚ΠΈΠ»ΡŒ ΠΊΠ½ΠΎΠΏΠΊΠΈ с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ .buttonStyle(CustomButtonStyle()).

Β Π”Ρ€ΡƒΠ³ΠΈΠ΅ встроСнныС view, для ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Ρ… ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΡΠΎΠ·Π΄Π°Π²Π°Ρ‚ΡŒ собствСнныС стили:

  • Toggle
  • DatePicker
  • Gauge
  • ProgressView
  • Label
  • LabeledContent
  • DisclosureGroup
  • ControlGroup
  • GroupBox
  • Form

ΠšΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΡ стиля Ρ‚Π°ΠΊΠΆΠ΅ ΠΈΠΌΠ΅Π΅Ρ‚ свойства, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΏΠΎΠ·Π²ΠΎΠ»ΡΡŽΡ‚ ΡΡ‚ΠΈΠ»ΡŽ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΡΡ‚ΡŒ, Π½Π°ΠΆΠ°Ρ‚Π° Π»ΠΈ ΠΊΠ½ΠΎΠΏΠΊΠ° ΠΈ ΠΊΠ°ΠΊΡƒΡŽ Ρ€ΠΎΠ»ΡŒ ΠΈΠ³Ρ€Π°Π΅Ρ‚ ΠΊΠ½ΠΎΠΏΠΊΠ°.
ΠœΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ эти свойства, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΠΏΡ€ΠΈΠ΄Π°Ρ‚ΡŒ ΠΊΠ½ΠΎΠΏΠΊΠ΅ Π΄Ρ€ΡƒΠ³ΠΎΠΉ Π²ΠΈΠ΄ ΠΏΡ€ΠΈ Π½Π°ΠΆΠ°Ρ‚ΠΈΠΈ ΠΈΠ»ΠΈ Ссли ΠΊΠ½ΠΎΠΏΠΊΠ° выполняСт дСструктивноС дСйствиС:

.background {
Β  Β  Capsule()
Β  Β  Β  Β  .stroke(configuration.role == .destructive ? .red : .yellow, lineWidth: 2)
}
.opacity(configuration.isPressed ? 0.5 : 1)

VStack(spacing: 16) {
Β  Β  Button(role: .destructive) {
Β  Β  Β  Β  // Delete
Β  Β  } label: {
Β  Β  Β  Β  Label("Delete", systemImage: "trash")
Β  Β  }
Β  Β  Button("Cancel", role: .cancel) {
Β  Β  Β  Β  // Cancel Deletion
Β  Β  }
}
.buttonStyle(CustomButtonStyle())


Π”Π²Π΅ ΠΊΠ½ΠΎΠΏΠΊΠΈ Π² VStack, ΠΎΠ΄Π½Π° с ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΡΠΊΠΈΠΌ стилСм ΠΊΠ½ΠΎΠΏΠΎΠΊ с красным ΠΊΠΎΠ½Ρ‚ΡƒΡ€ΠΎΠΌ ΠΈ ΠΎΠ΄Π½Π° с использованиСм ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΡΠΊΠΎΠ³ΠΎ стиля ΠΊΠ½ΠΎΠΏΠΎΠΊ с ΠΆΠ΅Π»Ρ‚Ρ‹ΠΌ ΠΊΠΎΠ½Ρ‚ΡƒΡ€ΠΎΠΌ.

Β 

Β 

Бтилизация ΠΎΡ‚ΠΊΠ»ΡŽΡ‡Π΅Π½Π½Ρ‹Ρ… состояний

ΠŸΡ€ΠΈΠΌΠ΅Ρ‡Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎ, Ρ‡Ρ‚ΠΎ Π² ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΠΈ отсутствуСт Ρ„Π»Π°Π³, ΡƒΠΊΠ°Π·Ρ‹Π²Π°ΡŽΡ‰ΠΈΠΉ, Π²Ρ‹ΠΊΠ»ΡŽΡ‡Π΅Π½Π° Π»ΠΈ ΠΊΠ½ΠΎΠΏΠΊΠ°. Π’Π°ΠΊΠΈΠΌ ΠΎΠ±Ρ€Π°Π·ΠΎΠΌ, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΡƒΡΡ‚Π°Π½ΠΎΠ²ΠΈΡ‚ΡŒ Π²Ρ‹ΠΊΠ»ΡŽΡ‡Π΅Π½Π½ΠΎΠ΅ состояниС ΠΊΠ½ΠΎΠΏΠΊΠΈ, ΠΌΡ‹ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌ ΠΌΠΎΠ΄ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ view disabled(_:).

ΠŸΠΎΡΠΊΠΎΠ»ΡŒΠΊΡƒ этот ΠΌΠΎΠ΄ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ view являСтся Ρ€Π°ΡΡˆΠΈΡ€Π΅Π½ΠΈΠ΅ΠΌ view, ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΡƒΡΡ‚Π°Π½ΠΎΠ²ΠΈΡ‚ΡŒ Π²Ρ‹ΠΊΠ»ΡŽΡ‡Π΅Π½Π½ΠΎΠ΅ состояниС Π² любом мСстС ΠΈΠ΅Ρ€Π°Ρ€Ρ…ΠΈΠΈ view. Π­Ρ‚ΠΎ ΡƒΠ΄ΠΎΠ±Π½ΠΎ, ΠΊΠΎΠ³Π΄Π°, Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, ΠΌΡ‹ Ρ…ΠΎΡ‚ΠΈΠΌ ΠΎΡ‚ΠΊΠ»ΡŽΡ‡ΠΈΡ‚ΡŒ всС элСмСнты управлСния Π²ΠΎ view Π²ΠΎ врСмя ΠΎΡ‚ΠΏΡ€Π°Π²ΠΊΠΈ Ρ„ΠΎΡ€ΠΌΡ‹.

Однако Π² Π΄ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚Π°Ρ†ΠΈΠΈ Π½Π΅ ΡƒΠΊΠ°Π·Π°Π½ΠΎ, Ρ‡Ρ‚ΠΎ этот ΠΌΠΎΠ΄ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ устанавливаСт Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ environment (срСды). Π’Π°ΠΊΠΈΠΌ ΠΎΠ±Ρ€Π°Π·ΠΎΠΌ, Ρ‡Ρ‚ΠΎΠ±Ρ‹ Π½Π°ΡΡ‚Ρ€ΠΎΠΈΡ‚ΡŒ ΠΊΠ½ΠΎΠΏΠΊΡƒ, ΠΊΠΎΠ³Π΄Π° ΠΎΠ½Π° ΠΎΡ‚ΠΊΠ»ΡŽΡ‡Π΅Π½Π°, Π½Π°ΠΌ Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΈΡ‚ΡŒ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ environment isEnabled:

@Environment(\.isEnabled) var isEnabled

func makeBody(configuration: Configuration) -> some View {
Β  Β  // ...
Β  Β  Β  Β  .saturation(isEnabled ? 1 : 0)
}

Кнопка Β«ΠžΡ‚ΠΏΡ€Π°Π²ΠΈΡ‚ΡŒΒ» с сСрым тСкстом ΠΈ сСрым ΠΊΠΎΠ½Ρ‚ΡƒΡ€ΠΎΠΌ Π² Π²ΠΈΠ΄Π΅ капсулы.ΠšΡƒΡ‡Π° ΠΏΠΎΠ»Π·ΡƒΠ½ΠΊΠΎΠ² Π΄ΠΈΠ°ΠΏΠ°Π·ΠΎΠ½Π° Π² Ρ€Π°Π·Π½Ρ‹Ρ… стилях.Β 

Β 

Бтилизация ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΡΠΊΠΈΡ… view

Π’ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡ‚ΡŒ ΡΠΎΠ·Π΄Π°Π²Π°Ρ‚ΡŒ собствСнныС стили для встроСнных view SwiftUI ΠΌΠΎΠΆΠ΅Ρ‚ Π±Ρ‹Ρ‚ΡŒ ΠΎΡ‡Π΅Π½ΡŒ ΠΏΠΎΠ»Π΅Π·Π½ΠΎΠΉ, ΠΊΠΎΠ³Π΄Π° ΠΎΠ½ΠΈ выглядят Π½Π΅ Ρ‚Π°ΠΊ, ΠΊΠ°ΠΊ Π½Π°ΠΌ Π±Ρ‹ Ρ‚ΠΎΠ³ΠΎ Ρ…ΠΎΡ‚Π΅Π»ΠΎΡΡŒ, Π½ΠΎ, ΠΊ соТалСнию, Π½Π΅Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎ ΡΠΎΠ·Π΄Π°Ρ‚ΡŒ собствСнныС стили для всСх встроСнных view SwiftUI.

Π₯отя ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΠΏΡ€ΠΈΠ±Π΅Π³Π½ΡƒΡ‚ΡŒ ΠΊ добавлСнию ΠΌΠΎΠ΄ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ΠΎΠ² view для встроСнного стиля Π² ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΡΠΊΠΈΡ… view ΠΈΠ»ΠΈ ΠΎΠ±ΠΎΠΉΡ‚ΠΈΡΡŒ Ρ‚Π΅ΠΌ, Ρ‡Ρ‚ΠΎ прСдоставляСт SwiftUI, ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ Π΄ΠΎΠ±ΠΈΡ‚ΡŒΡΡ большСго успСха, сдСлав наши настраиваСмыС view стилизуСмыми. Π”Π°Π²Π°ΠΉΡ‚Π΅ ΠΏΠΎΠΏΡ€ΠΎΠ±ΡƒΠ΅ΠΌ это Π½Π° ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΡΠΊΠΎΠΌ элСмСнтС управлСния RangeSlider:

struct RangeSlider: View {
Β  Β  @Binding
Β  Β  var range: ClosedRange

Β  Β  var bounds: ClosedRange?

Β  Β  var label: Label

Β  Β  init(value: Binding>, in bounds: ClosedRange? = nil, @ViewBuilder label: () -> Label) {
Β  Β  Β  Β  self._value = value
Β  Β  Β  Β  self.bounds = bounds
Β  Β  Β  Β  self.label = label()
Β  Β  }

Β  Β  var body: some View {
Β  Β  Β  Β  LabeledContent {
Β  Β  Β  Β  Β  Β  // ...
Β  Β  Β  Β  } label: {
Β  Β  Β  Β  Β  Β  Label
Β  Β  Β  Β  }
Β  Β  }
}

ΠŸΠΎΠ»Π·ΡƒΠ½ΠΎΠΊ Π΄ΠΈΠ°ΠΏΠ°Π·ΠΎΠ½Π° со стилСм, ΠΏΠΎΡ…ΠΎΠΆΠΈΠΌ Π½Π° встроСнный элСмСнт управлСния ΠΏΠΎΠ»Π·ΡƒΠ½ΠΊΠΎΠΌ.

Π˜Ρ‚Π°ΠΊ, Ρ‡Ρ‚ΠΎ Π΄ΠΎΠ»ΠΆΠ½ΠΎ ΠΏΡ€ΠΎΠΈΠ·ΠΎΠΉΡ‚ΠΈ, Ρ‡Ρ‚ΠΎΠ±Ρ‹ это view ΠΌΠΎΠΆΠ½ΠΎ Π±Ρ‹Π»ΠΎ ΡΡ‚ΠΈΠ»ΠΈΠ·ΠΎΠ²Π°Ρ‚ΡŒ?

Π’ΠΎΠ·Π²Ρ€Π°Ρ‰Π°ΡΡΡŒ ΠΊ API стиля SwiftUI для ΠΊΠ½ΠΎΠΏΠΊΠΈ, ΠΌΡ‹ Π²ΠΈΠ΄ΠΈΠΌ, Ρ‡Ρ‚ΠΎ Ρƒ Π½Π΅Π³ΠΎ Π΅ΡΡ‚ΡŒ Π΄Π²Π° ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ»Π° для оформлСния ΠΊΠ½ΠΎΠΏΠΊΠΈ.

ButtonStyle ΡƒΠΏΡ€ΠΎΡ‰Π°Π΅Ρ‚ созданиС Π½ΠΎΠ²Ρ‹Ρ… стилСй, Ρ‚Π°ΠΊ ΠΊΠ°ΠΊ позволяСт Button ΠΏΠΎΠ·Π°Π±ΠΎΡ‚ΠΈΡ‚ΡŒΡΡ ΠΎΠ± ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ΅ ТСстов, Π° PrimitiveButtonStyle Π΄Π°Ρ‘Ρ‚ ΡΡ‚ΠΈΠ»ΡŽ ΠΊΠΎΠ½Ρ‚Ρ€ΠΎΠ»ΡŒ Π½Π°Π΄ Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠ΅ΠΉ взаимодСйствия с ΠΊΠ½ΠΎΠΏΠΊΠΎΠΉ.

Π”Ρ€ΡƒΠ³ΠΈΠ΅ ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ»Ρ‹ стилСй Π² SwiftUI Π½Π°ΠΏΠΎΠΌΠΈΠ½Π°ΡŽΡ‚ ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ» PrimitiveButtonStyle Ρ‚Π΅ΠΌ, Ρ‡Ρ‚ΠΎ ΠΎΠ½ΠΈ Ρ‚Π°ΠΊΠΆΠ΅ Π΄Π°ΡŽΡ‚ ΠΊΠΎΠ½Ρ‚Ρ€ΠΎΠ»ΡŒ Π½Π°Π΄ взаимодСйствиСм со стилСм. НапримСр, ΠΏΡ€ΠΈ Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ ToggleStyle view Toggle Π½Π΅ ΠΎΠ±Ρ€Π°Π±Π°Ρ‚Ρ‹Π²Π°Π΅Ρ‚ ТСст Π·Π° нас. ВмСсто этого ΠΎΠ½ прСдоставляСт привязку для управлСния своим Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ΠΌ, ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ΅ ΡΡ‚ΠΈΠ»ΡŒ Π΄ΠΎΠ»ΠΆΠ΅Π½ ΠΎΠ±Π½ΠΎΠ²Π»ΡΡ‚ΡŒ, ΠΊΠΎΠ³Π΄Π° ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒ касаСтся view.

Π§Ρ‚ΠΎΠ±Ρ‹ Π΄Π°Ρ‚ΡŒ большС контроля Π½Π°Π΄ стилями ΠΏΠΎΠ»Π·ΡƒΠ½ΠΊΠ° Π΄ΠΈΠ°ΠΏΠ°Π·ΠΎΠ½Π°, ΠΎΠΏΡ€Π΅Π΄Π΅Π»ΠΈΡ‚ΡŒ, ΠΊΠ°ΠΊΠΈΠΌ Π΄ΠΎΠ»ΠΆΠ½ΠΎ Π±Ρ‹Ρ‚ΡŒ взаимодСйствиС, ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΠΎΡΠ½ΠΎΠ²Ρ‹Π²Π°Ρ‚ΡŒ наш ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ» стилСй Π½Π° ΠΏΡ€ΠΈΠΌΠΈΡ‚ΠΈΠ²Π½ΠΎΠΉ вСрсии API стилСй ΠΊΠ½ΠΎΠΏΠΎΠΊ SwiftUI.

API-интСрфСйсы стилСй состоят ΠΈΠ· Ρ‚Ρ€Π΅Ρ… частСй β€” ΠΌΠΎΠ΄ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€Π° view, ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌΠΎΠ³ΠΎ для установки стиля, ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ»Π° ΠΈ ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΠΈ стиля:

extension View {
Β  Β  public func buttonStyle(_ style: S) -> some View where S : PrimitiveButtonStyle
}

Β 

public protocol PrimitiveButtonStyle {
Β  Β  associatedtype Body : View

Β  Β  @ViewBuilder func makeBody(configuration: Self.Configuration) -> Self.Body

Β  Β  typealias Configuration = PrimitiveButtonStyleConfiguration
}

Β 

public struct PrimitiveButtonStyleConfiguration {
Β  Β  // ...

Β  Β  public let role: ButtonRole?

Β  Β  public let label: PrimitiveButtonStyleConfiguration.Label

Β  Β  public func trigger()
}

Β 

Π§Ρ‚ΠΎΠ±Ρ‹ ΡΡ‚ΠΈΠ»ΠΈΠ·ΠΎΠ²Π°Ρ‚ΡŒ RangeSlider Ρ‚Π°ΠΊ ΠΆΠ΅, ΠΊΠ°ΠΊ ΠΈ встроСнныС view, ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΡΠΊΠΎΠΏΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ Π±ΠΎΠ»ΡŒΡˆΡƒΡŽ Ρ‡Π°ΡΡ‚ΡŒ ΠΊΠΎΠ΄Π° Π²Ρ‹ΡˆΠ΅.

ΠœΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ Π½Π°Ρ‡Π°Ρ‚ΡŒ с опрСдСлСния нашСго собствСнного Ρ‚ΠΈΠΏΠ° ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΠΈ стиля.

Бвойства PrimitiveButtonStyleConfiguration ΠΌΠΎΠ³ΡƒΡ‚ ΠΏΠΎΠΊΠ°Π·Π°Ρ‚ΡŒΡΡ Π½Π΅ΠΌΠ½ΠΎΠ³ΠΎ Π·Π½Π°ΠΊΠΎΠΌΡ‹ΠΌΠΈ, ΠΏΠΎΡΠΊΠΎΠ»ΡŒΠΊΡƒ ΠΎΠ½ΠΈ ΠΏΠΎΡ‡Ρ‚ΠΈ Π½Π°ΠΏΡ€ΡΠΌΡƒΡŽ ΡΠΎΠΏΠΎΡΡ‚Π°Π²Π»ΡΡŽΡ‚ΡΡ с Ρ‚ΠΈΠΏΠ°ΠΌΠΈ ΠΎΠ΄Π½ΠΎΠ³ΠΎ ΠΈΠ· ΠΈΠ½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·Π°Ρ‚ΠΎΡ€ΠΎΠ² Button:

public init(
Β  Β  role: ButtonRole?,
Β  Β  action: @escaping () -> Void,
Β  Β  @ViewBuilder label: () -> Label
)

Β 

role пСрСдаСтся β€œΠΊΠ°ΠΊ Π΅ΡΡ‚ΡŒβ€, action прСдставлСно ΠΊΠ°ΠΊ триггСрная функция, Π° label прСдставляСт собой view со стёртым Ρ‚ΠΈΠΏΠΎΠΌ.

Π’Π°ΠΊΠΈΠΌ ΠΎΠ±Ρ€Π°Π·ΠΎΠΌ, для нашСго собствСнного Ρ‚ΠΈΠΏΠ° ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΠΈ ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ Ρ‚ΠΎΡ‚ ΠΆΠ΅ ΠΏΠΎΠ΄Ρ…ΠΎΠ΄ ΠΈ ΡΠΊΠΎΠΏΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ Ρ‚ΠΈΠΏΡ‹ ΠΈΠ· нашСго view Π² ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΡŽ:

struct RangeSliderStyleConfiguration {
Β  Β  @Binding
Β  Β  let range: ClosedRange

Β  Β  let bounds: ClosedRange?

Β  Β  let label: Label
}

Β 

Как ΠΌΡ‹ Π²ΠΈΠ΄Π΅Π»ΠΈ Π² случаС с ΠΎΡ‚ΠΊΠ»ΡŽΡ‡Π΅Π½Π½Ρ‹ΠΌ состояниСм для ΠΊΠ½ΠΎΠΏΠΊΠΈ, Ρƒ нас Π½Π΅Ρ‚ нСобходимости ΠΏΠΎΠΌΠ΅Ρ‰Π°Ρ‚ΡŒ значСния, доступныС Π² environment, Π² ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΡŽ стиля. ВмСсто этого стили Π΄ΠΎΠ»ΠΆΠ½Ρ‹ ΠΈΠΌΠ΅Ρ‚ΡŒ прямой доступ ΠΊ значСниям environment.

ΠŸΡ€ΠΈΠ²Π΅Π΄Π΅Π½Π½Ρ‹ΠΉ Π²Ρ‹ΡˆΠ΅ ΠΊΠΎΠ΄ прост для значСния ΠΈ Π³Ρ€Π°Π½ΠΈΡ†, ΠΏΠΎΡΠΊΠΎΠ»ΡŒΠΊΡƒ ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ просто ΡΠΊΠΎΠΏΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ ΠΈΡ… ΠΊΠ°ΠΊ Π΅ΡΡ‚ΡŒ. Однако для ΠΌΠ΅Ρ‚ΠΊΠΈ Π½Π°ΠΌ Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ Ρ‡Ρ‚ΠΎ-Ρ‚ΠΎ Π΄Ρ€ΡƒΠ³ΠΎΠ΅, Ρ‚Π°ΠΊ ΠΊΠ°ΠΊ этот Ρ‚ΠΈΠΏ Ρ‚Π΅ΠΏΠ΅Ρ€ΡŒ относится ΠΊ Ρ‚ΠΈΠΏΡƒ ΠΌΠ΅Ρ‚ΠΊΠΈ SwiftUI, Π° Π½Π΅ ΠΊ ΡƒΠ½ΠΈΠ²Π΅Ρ€ΡΠ°Π»ΡŒΠ½ΠΎΠΌΡƒ Ρ‚ΠΈΠΏΡƒ, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ RangeSlider ΠΈΠΌΠ΅Π΅Ρ‚ для своСй ΠΌΠ΅Ρ‚ΠΊΠΈ.

Кнопка Ρ‚Π°ΠΊΠΆΠ΅ ΠΈΠΌΠ΅Π΅Ρ‚ ΠΎΠ±Ρ‰ΠΈΠΉ Ρ‚ΠΈΠΏ для своСй ΠΌΠ΅Ρ‚ΠΊΠΈ, Ρ‡Ρ‚ΠΎ позволяСт Π½Π°ΠΌ ΡΠΎΠ·Π΄Π°Π²Π°Ρ‚ΡŒ ΠΊΠ½ΠΎΠΏΠΊΠΈ, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡŽΡ‚ Ρ€Π°Π·Π½Ρ‹Π΅ Ρ‚ΠΈΠΏΡ‹ view для своих ΠΌΠ΅Ρ‚ΠΎΠΊ. ΠœΠ΅Ρ‚ΠΊΠ° ΠΊΠ½ΠΎΠΏΠΊΠΈ часто ΠΈΠΌΠ΅Π΅Ρ‚ Ρ‚ΠΈΠΏ Text, Label ΠΈΠ»ΠΈ HStack, Π½ΠΎ ΠΌΠΎΠΆΠ΅Ρ‚ Π±Ρ‹Ρ‚ΡŒ любого Ρ‚ΠΈΠΏΠ°.

ΠŸΠΎΡΠΊΠΎΠ»ΡŒΠΊΡƒ сущСствуСт Π½Π΅ΠΎΠ³Ρ€Π°Π½ΠΈΡ‡Π΅Π½Π½ΠΎΠ΅ количСство Π²ΠΎΠ·ΠΌΠΎΠΆΠ½Ρ‹Ρ… Ρ‚ΠΈΠΏΠΎΠ² ΠΊΠ½ΠΎΠΏΠΎΠΊ, SwiftUI ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ стираниС Ρ‚ΠΈΠΏΠΎΠ², Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΡΠΊΡ€Ρ‹Ρ‚ΡŒ ΠΈΡ… всС Π·Π° Π½Π΅ΠΏΡ€ΠΎΠ·Ρ€Π°Ρ‡Π½Ρ‹ΠΌ view ButtonConfiguration.Label. Π­Ρ‚ΠΎ ΠΈΠ·ΠΎΠ»ΠΈΡ€ΡƒΠ΅Ρ‚ ΡΡ‚ΠΈΠ»ΡŒ ΠΎΡ‚ ΠΊΠΎΠ½ΠΊΡ€Π΅Ρ‚Π½ΠΎΠ³ΠΎ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ΠΈΠ·ΠΎΠ²Π°Π½Π½ΠΎΠ³ΠΎ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Π° ΠΈ позволяСт ΠΏΡ€ΠΈΠΌΠ΅Π½ΠΈΡ‚ΡŒ Π΅Π΄ΠΈΠ½Ρ‹ΠΉ ΡΡ‚ΠΈΠ»ΡŒ ΠΊ любой Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΠΉ ΠΊΠ½ΠΎΠΏΠΊΠ΅.

ΠœΡ‹ Π±ΡƒΠ΄Π΅ΠΌ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ Ρ‚ΠΎΡ‚ ΠΆΠ΅ шаблон, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΡΠΎΡ…Ρ€Π°Π½ΠΈΡ‚ΡŒ ΠΎΠ±Ρ‰ΠΈΠΉ RangeSlider Π½Π°Π΄ своСй ΠΌΠ΅Ρ‚ΠΊΠΎΠΉ, ΠΈ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŽ API стиля Π½Π΅ Π½ΡƒΠΆΠ½ΠΎ Π·Π½Π°Ρ‚ΡŒ, ΠΊΠ°ΠΊΠΈΠ΅ ΠΊΠΎΠ½ΠΊΡ€Π΅Ρ‚Π½Ρ‹Π΅ ΠΌΠ΅Ρ‚ΠΊΠΈ Π±ΡƒΠ΄ΡƒΡ‚ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒΡΡ:

/// A type-erased label of a button.
public struct Label : View {
Β  Β  public typealias Body = Never
}

Β 

SwiftUI опрСдСляСт Ρ‚ΠΈΠΏ Body ΠΊΠ°ΠΊ Never, это ΠΎΠ·Π½Π°Ρ‡Π°Π΅Ρ‚, Ρ‡Ρ‚ΠΎ ΠΎΠ½ Π±ΡƒΠ΄Π΅Ρ‚ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ частныС API для отобраТСния view ΠΌΠ΅Ρ‚ΠΊΠΈ.

ΠœΡ‹ Π½Π΅ ΠΌΠΎΠΆΠ΅ΠΌ ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ Ρ‚ΠΎ, Ρ‡Ρ‚ΠΎ Π΄Π΅Π»Π°Π΅Ρ‚ здСсь SwiftUI, Π½ΠΎ ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ AnyView для получСния Ρ‚ΠΎΠ³ΠΎ ΠΆΠ΅ эффСкта:

/// A type-erased label of a range slider.
struct Label: View {
Β  Β  let underlyingLabel: AnyView

Β  Β  init(_ label: some View) {
Β  Β  Β  Β  self.underlyingLabel = AnyView(label)
Β  Β  }

Β  Β  var body: some View {
Β  Β  Β  Β  underlyingLabel
Β  Β  }
}

Β 

Π₯отя ΠΌΡ‹ Ρ‚Π°ΠΊΠΆΠ΅ ΠΌΠΎΠΆΠ΅ΠΌ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ AnyView Π½Π°ΠΏΡ€ΡΠΌΡƒΡŽ, это Π΄Π°Π΅Ρ‚ Π½Π°ΠΌ Ρ‚ΠΈΠΏ, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ для прСдоставлСния Π΄ΠΎΠΏΠΎΠ»Π½ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹Ρ… Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΉ стилям. НапримСр, Ρƒ нас ΠΌΠΎΠΆΠ΅Ρ‚ Π±Ρ‹Ρ‚ΡŒ свойство для тСкста-заполнитСля тСкстового поля со стёртым ΡˆΡ€ΠΈΡ„Ρ‚ΠΎΠΌ, ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ΅ ΠΌΠΎΠΆΠ½ΠΎ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ Π² стилС ΠΏΠ»Π°Π²Π°ΡŽΡ‰Π΅ΠΉ ΠΌΠ΅Ρ‚ΠΊΠΈ.

Для ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ»Π° стилСй ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΡΠΊΠΎΠΏΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ Ρ‚ΠΎ, Ρ‡Ρ‚ΠΎ Π΄Π΅Π»Π°Π΅Ρ‚ SwiftUI:

protocol RangeSliderStyle {
Β  Β  associatedtype Body: View

Β  Β  @ViewBuilder func makeBody(configuration: Configuration) -> Body

Β  Β  typealias Configuration = RangeSliderStyleConfiguration
}

Β 

ИмСя ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ», ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ Ρ€Π΅Π°Π»ΠΈΠ·ΠΎΠ²Π°Ρ‚ΡŒ ΠΏΠ΅Ρ€Π²Ρ‹ΠΉ ΡΡ‚ΠΈΠ»ΡŒ, ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡ значСния ΠΈΠ· ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΠΈ стиля:

struct DefaultRangeSliderStyle: RangeSliderStyle {
Β  Β  func makeBody(configuration: Configuration) -> some View {
Β  Β  Β  Β  GeometryReader { proxy in
Β  Β  Β  Β  Β  Β  ZStack {
Β  Β  Β  Β  Β  Β  Β  Β  Capsule()
Β  Β  Β  Β  Β  Β  Β  Β  Β  Β  .fill(.regularMaterial)
Β  Β  Β  Β  Β  Β  Β  Β  Β  Β  .frame(height: 4)
Β  Β  Β  Β  Β  Β  Β  Β  Capsule()
Β  Β  Β  Β  Β  Β  Β  Β  Β  Β  .fill(.tint)
Β  Β  Β  Β  Β  Β  Β  Β  Β  Β  .frame(height: 4)
Β  Β  Β  Β  Β  Β  Β  Β  Β  Β  // Width and position...
Β  Β  Β  Β  Β  Β  Β  Β  Circle()
Β  Β  Β  Β  Β  Β  Β  Β  Β  Β  .frame(width: 27, height: 27)
Β  Β  Β  Β  Β  Β  Β  Β  Β  Β  // Gesture handling and positioning...
Β  Β  Β  Β  Β  Β  Β  Β  Circle()
Β  Β  Β  Β  Β  Β  Β  Β  Β  Β  .frame(width: 27, height: 27)
Β  Β  Β  Β  Β  Β  Β  Β  Β  Β  // Gesture handling and positioning...
Β  Β  Β  Β  Β  Β  }
Β  Β  Β  Β  }
Β  Β  Β  Β  .frame(height: 27)
Β  Β  }
}

ΠŸΠΎΠ»Π·ΡƒΠ½ΠΎΠΊ Π΄ΠΈΠ°ΠΏΠ°Π·ΠΎΠ½Π° со стилСм, ΠΏΠΎΡ…ΠΎΠΆΠΈΠΌ Π½Π° встроСнный элСмСнт управлСния ΠΏΠΎΠ»Π·ΡƒΠ½ΠΊΠΎΠΌ.Β 

Π§Ρ‚ΠΎΠ±Ρ‹ ΡƒΡΡ‚Π°Π½ΠΎΠ²ΠΈΡ‚ΡŒ ΡΡ‚ΠΈΠ»ΡŒ для ΠΈΠ΅Ρ€Π°Ρ€Ρ…ΠΈΠΈ view, ΠΌΡ‹ добавляСм Ρ€Π°ΡΡˆΠΈΡ€Π΅Π½ΠΈΠ΅ ΠΊ View, ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ΅ соотвСтствуСт сигнатурС buttonStyle(_:), Π½ΠΎ ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ ΠΊΠ»ΡŽΡ‡Π΅Π²ΠΎΠ΅ слово some, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ Π΅Π³ΠΎ Π½Π΅ΠΌΠ½ΠΎΠ³ΠΎ Π±ΠΎΠ»Π΅Π΅ ΠΊΡ€Π°Ρ‚ΠΊΠΈΠΌ:

extension View {
Β  Β  func rangeSliderStyle(_ style: some RangeSliderStyle) -> some View {
Β  Β  Β  Β  environment(\.rangeSliderStyle, style)
Β  Β  }
}

Β 

ВстроСнныС стили ΡΠΏΡƒΡΠΊΠ°ΡŽΡ‚ΡΡ Π²Π½ΠΈΠ· ΠΏΠΎ ΠΈΠ΅Ρ€Π°Ρ€Ρ…ΠΈΠΈ view, ΠΊΠ°ΠΊ ΠΈ значСния environment. Π’Π°ΠΊΠΈΠΌ ΠΎΠ±Ρ€Π°Π·ΠΎΠΌ, ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ environment для ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‡ΠΈ стиля Ρ‡Π΅Ρ€Π΅Π· ΠΈΠ΅Ρ€Π°Ρ€Ρ…ΠΈΡŽ view Π²ΠΎ view, ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ΅ Π΄ΠΎΠ»ΠΆΠ½ΠΎ Π±Ρ‹Ρ‚ΡŒ ΠΎΡ„ΠΎΡ€ΠΌΠ»Π΅Π½ΠΎ. ΠŸΡ€ΠΈ этом ΠΌΡ‹ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΠΌ Ρ‚ΠΎ ΠΆΠ΅ ΠΏΠΎΠ²Π΅Π΄Π΅Π½ΠΈΠ΅ распространСния стиля, Ρ‡Ρ‚ΠΎ ΠΈ встроСнныС стили.
Π§Ρ‚ΠΎΠ±Ρ‹ ΠΈΠΌΠ΅Ρ‚ΡŒ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡ‚ΡŒ ΠΏΠΎΠΌΠ΅ΡΡ‚ΠΈΡ‚ΡŒ любой Ρ‚ΠΈΠΏ стиля, ΡΠΎΠΎΡ‚Π²Π΅Ρ‚ΡΡ‚Π²ΡƒΡŽΡ‰ΠΈΠΉ Π½Π°ΡˆΠ΅ΠΌΡƒ ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ»Ρƒ, Π² срСду Π² качСствС значСния, Π½Π°ΠΌ Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ Ρ‚ΠΈΠΏ значСния environment Π»ΡŽΠ±Ρ‹ΠΌ RangeSliderStyle.

Для Π·Π½Π°Ρ‡Π΅Π½ΠΈΠΉ environment трСбуСтся ΠΎΠΏΡ€Π΅Π΄Π΅Π»Ρ‘Π½Π½ΠΎΠ΅ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ ΠΏΠΎ ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽ. Π‘Ρ‚ΠΈΠ»ΡŒΠ½Ρ‹Π΅ view SwiftUI всСгда ΠΈΠΌΠ΅ΡŽΡ‚ ΡΡ‚ΠΈΠ»ΡŒ ΠΏΠΎ ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽ, поэтому ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΠΎΠΏΡ€Π΅Π΄Π΅Π»ΠΈΡ‚ΡŒ, ΠΊΠ°ΠΊΠΈΠΌ ΠΎΠ½ Π΄ΠΎΠ»ΠΆΠ΅Π½ Π±Ρ‹Ρ‚ΡŒ для view здСсь:

struct RangeSliderStyleKey: EnvironmentKey {
Β  Β  static var defaultValue: any RangeSliderStyle = DefaultRangeSliderStyle()
}

extension EnvironmentValues {
Β  Β  var rangeSliderStyle: any RangeSliderStyle {
Β  Β  Β  Β  get { self[RangeSliderStyleKey.self] }
Β  Β  Β  Β  set { self[RangeSliderStyleKey.self] = newValue }
Β  Β  }
}

Β 

ИмСя Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ environment, ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ Ρ€Π΅Π°Π»ΠΈΠ·ΠΎΠ²Π°Ρ‚ΡŒ body нашСго view, создав ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΡŽ стиля с Π²Ρ…ΠΎΠ΄Π½Ρ‹ΠΌΠΈ Π΄Π°Π½Π½Ρ‹ΠΌΠΈ ΠΈΠ· нашСго ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Π°, Π° Π·Π°Ρ‚Π΅ΠΌ Π²Ρ‹Π·Π²Π°Π² ΠΌΠ΅Ρ‚ΠΎΠ΄ makeBody для стиля, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ ΠΌΡ‹ ΠΏΠΎΠ»ΡƒΡ‡Π°Π΅ΠΌ ΠΈΠ· environment:

struct RangeSlider: View {
Β  Β  // ...
Β  Β  @Environment(\.rangeSliderStyle) var style

Β  Β  var body: some View {
Β  Β  Β  Β  let configuration = RangeSliderStyleConfiguration(
Β  Β  Β  Β  Β  Β  range: $range,
Β  Β  Β  Β  Β  Β  bounds: bounds,
Β  Β  Β  Β  Β  Β  label: .init(label)
Β  Β  Β  Β  )

Β  Β  Β  Β  style.makeBody(configuration: configuration)
Β  Β  }
}


Однако, Ссли ΠΌΡ‹ попытаСмся Π·Π°ΠΏΡƒΡΡ‚ΠΈΡ‚ΡŒ этот ΠΊΠΎΠ΄, ΠΌΡ‹ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΠΌ ΠΎΡˆΠΈΠ±ΠΊΡƒ компилятора.


Β 
ΠŸΠΎΡΠΊΠΎΠ»ΡŒΠΊΡƒ body ΠΎΠΆΠΈΠ΄Π°Π΅Ρ‚, Ρ‡Ρ‚ΠΎ ΠΌΡ‹ Π²Π΅Ρ€Π½Ρ‘ΠΌ ΠΊΠΎΠ½ΠΊΡ€Π΅Ρ‚Π½Ρ‹ΠΉ Ρ‚ΠΈΠΏ view, Π° style, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ ΠΌΡ‹ ΠΏΠΎΠ»ΡƒΡ‡Π°Π΅ΠΌ ΠΈΠ· срСды, ΠΈΠΌΠ΅Π΅Ρ‚ Ρ‚ΠΈΠΏ any RangeSliderStyle, Π½Π°ΠΌ Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ ΠΎΠ±Π΅Ρ€Π½ΡƒΡ‚ΡŒ view, ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ΅ ΠΌΡ‹ ΠΏΠΎΠ»ΡƒΡ‡Π°Π΅ΠΌ ΠΎΡ‚ Π²Ρ‹Π·ΠΎΠ²Π° makeBody, Π² AnyView:

AnyView(style.makeBody(configuration: configuration))

Β 

Π­Ρ‚ΠΎ заставляСт Π΅Π³ΠΎ снова ΠΊΠΎΠΌΠΏΠΈΠ»ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒΡΡ. Π’Π΅ΠΏΠ΅Ρ€ΡŒ, ΠΊΠΎΠ³Π΄Π° всё Π½Π° своих мСстах, Ρƒ нас Π΅ΡΡ‚ΡŒ настраиваСмый Π²ΠΈΠ΄ ΠΏΠΎΠ»Π·ΡƒΠ½ΠΊΠ° Π΄ΠΈΠ°ΠΏΠ°Π·ΠΎΠ½Π°, ΡΡ‚ΠΈΠ»ΡŒ ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ³ΠΎ ΠΌΠΎΠΆΠ΅Ρ‚ Π±Ρ‹Ρ‚ΡŒ ΠΎΡ„ΠΎΡ€ΠΌΠ»Π΅Π½ Ρ‚ΠΈΠΏΠΎΠΌ, ΡΠΎΠΎΡ‚Π²Π΅Ρ‚ΡΡ‚Π²ΡƒΡŽΡ‰ΠΈΠΌ ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ»Ρƒ стилСй, ΠΈ ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΡƒΡΡ‚Π°Π½ΠΎΠ²ΠΈΡ‚ΡŒ, ΠΊΠ°ΠΊΠΎΠΉ ΡΡ‚ΠΈΠ»ΡŒ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ, Ρ‚Π°ΠΊ ΠΆΠ΅, ΠΊΠ°ΠΊ ΠΌΡ‹ установили Π±Ρ‹ ΡΡ‚ΠΈΠ»ΡŒ для встроСнного view SwiftUI:

Form {
Β  Β  // ...
Β  Β  RangeSlider(range: $sizeRange, in: minSize...maxSize) {
Β  Β  Β  Β  Text("Size Range")
Β  Β  }
Β  Β  // ...
}.rangeSliderStyle(VerticalRangeSliderStyle())

ΠŸΠΎΠ»Π·ΡƒΠ½ΠΎΠΊ Π²Π΅Ρ€Ρ‚ΠΈΠΊΠ°Π»ΡŒΠ½ΠΎΠ³ΠΎ Π΄ΠΈΠ°ΠΏΠ°Π·ΠΎΠ½Π° со скруглСнными ΡƒΠ³Π»Π°ΠΌΠΈ ΠΈ синим Ρ†Π²Π΅Ρ‚ΠΎΠΌ Π·Π°Π»ΠΈΠ²ΠΊΠΈ для Π²Ρ‹Π±Ρ€Π°Π½Π½ΠΎΠ³ΠΎ Π΄ΠΈΠ°ΠΏΠ°Π·ΠΎΠ½Π°.

И Ρ‚ΠΎΡ‡Π½ΠΎ Ρ‚Π°ΠΊ ΠΆΠ΅, ΠΊΠ°ΠΊ с ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΡΠΊΠΈΠΌΠΈ стилями для стилСй SwiftUI, ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ Π΄ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ статичСский Ρ‡Π»Π΅Π½ Π² ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ» стилСй. Π­Ρ‚ΠΎ позволяСт Π½Π°ΠΌ ΡƒΡΡ‚Π°Π½ΠΎΠ²ΠΈΡ‚ΡŒ ΡΡ‚ΠΈΠ»ΡŒ с Ρ‚Π΅ΠΌ ΠΆΠ΅ сокращённым синтаксисом, Ρ‡Ρ‚ΠΎ ΠΈ встроСнныС стили, благодаря Ρ‡Π΅ΠΌΡƒ наши ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΡΠΊΠΈΠ΅ view ΡΠΎΠΎΡ‚Π²Π΅Ρ‚ΡΡ‚Π²ΡƒΡŽΡ‚ встроСнным view:

extension RangeSliderStyle where Self == VerticalRangeSliderStyle {
Β  Β  static var vertical: Self { .init() }
}

.rangeSliderStyle(.vertical)

Β 

Β 

Π”ΠΎΡΡ‚ΡƒΠΏΠ½ΠΎΡΡ‚ΡŒ

ΠŸΡ€ΠΈ создании ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΡΠΊΠΈΡ… view Π²Π°ΠΆΠ½ΠΎ Ρ‚Π°ΠΊΠΆΠ΅ ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ ΠΈΡ… доступными. ΠœΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ это, Π΄ΠΎΠ±Π°Π²ΠΈΠ² ΠΌΠΎΠ΄ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€Ρ‹ view, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΡƒΠ»ΡƒΡ‡ΡˆΠ°ΡŽΡ‚ Ρ€Π°Π±ΠΎΡ‚Ρƒ ΠΏΡ€ΠΈ использовании view, Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, с VoiceOver:

struct MySlider: View {
Β  Β  // ...

Β  Β  var body: some View {
Β  Β  Β  Β  // ...
Β  Β  Β  Β  AnyView(style.makeBody(configuration: configuration))
Β  Β  Β  Β  Β  Β  .accessibilityElement(children: .combine)
Β  Β  Β  Β  Β  Β  .accessibilityValue(valueText)
Β  Β  Β  Β  Β  Β  .accessibilityAdjustableAction { direction in
Β  Β  Β  Β  Β  Β  Β  Β  switch direction {
Β  Β  Β  Β  Β  Β  Β  Β  case .increment: increment()
Β  Β  Β  Β  Β  Β  Β  Β  case .decrement: decrement()
Β  Β  Β  Β  Β  Β  Β  Β  @unknown default:
Β  Β  Β  Β  Β  Β  Β  Β  Β  Β  break
Β  Β  Β  Β  Β  Β  Β  Β  }
Β  Β  Β  Β  Β  Β  }
Β  Β  }

Β  Β  var valueText: Text {
Β  Β  Β  Β  if bounds == 0.0...1.0 {
Β  Β  Β  Β  Β  Β  Β return Text(value.wrappedValue, format: .percent)
Β  Β  Β  Β  Β } else {
Β  Β  Β  Β  Β  Β  Β return Text(value.wrappedValue, format: .number)
Β  Β  Β  Β  Β }
Β  Β  }
}

Β Π“ΠΎΡ€ΠΈΠ·ΠΎΠ½Ρ‚Π°Π»ΡŒΠ½Ρ‹ΠΉ ΠΏΠΎΠ»Π·ΡƒΠ½ΠΎΠΊ громкости с ΠΎΡ€Π°Π½ΠΆΠ΅Π²ΠΎΠΉ Π·Π°Π»ΠΈΠ²ΠΊΠΎΠΉ для обозначСния уровня ΠΈ Π·Π½Π°Ρ‡ΠΎΠΊ Π΄ΠΈΠ½Π°ΠΌΠΈΠΊΠ° с ΠΊΠ°ΠΆΠ΄ΠΎΠΉ стороны.

β€œΠ“Ρ€ΠΎΠΌΠΊΠΎΡΡ‚ΡŒ 50%, рСгулируСмая. ΠŸΡ€ΠΎΠ²Π΅Π΄ΠΈΡ‚Π΅ Π²Π²Π΅Ρ€Ρ… ΠΈΠ»ΠΈ Π²Π½ΠΈΠ· ΠΎΠ΄Π½ΠΈΠΌ ΠΏΠ°Π»ΡŒΡ†Π΅ΠΌ, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΠΎΡ‚Ρ€Π΅Π³ΡƒΠ»ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ значСниС”.
- Голос Π·Π° ΠΊΠ°Π΄Ρ€ΠΎΠΌ

Π—Π΄Π΅ΡΡŒ ΠΌΡ‹ ΠΏΡ€Π΅Π΄ΠΏΠΎΠ»Π°Π³Π°Π΅ΠΌ, Ρ‡Ρ‚ΠΎ Π±ΠΎΠ»ΡŒΡˆΠΈΠ½ΡΡ‚Π²ΠΎ ΠΏΠΎΠ»Π·ΡƒΠ½ΠΊΠΎΠ² захотят ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ accessibilityAdjustableAction(_:), поэтому ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ Π΄ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ этот ΠΌΠΎΠ΄ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ Π²ΠΎ view ΠΏΠΎΠ»Π·ΡƒΠ½ΠΊΠ°. Π’Π°ΠΊΠΈΠΌ ΠΎΠ±Ρ€Π°Π·ΠΎΠΌ, ΠΊΠΎΠΌΠ°Π½Π΄Π°ΠΌ, Ρ€Π°Π±ΠΎΡ‚Π°ΡŽΡ‰ΠΈΠΌ Π½Π°Π΄ стилями, Π½Π΅Ρ‚ нСобходимости Π±Π΅ΡΠΏΠΎΠΊΠΎΠΈΡ‚ΡŒΡΡ ΠΎ Ρ‚ΠΎΠΌ, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ ΡΡ‚ΠΈΠ»ΡŒ доступным, ΠΏΠΎΡ‚ΠΎΠΌΡƒ Ρ‡Ρ‚ΠΎ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ view ΡƒΠΆΠ΅ доступСн.

ΠšΡƒΡ‡Π° ΠΏΠ΅Ρ€Π΅ΠΊΠ»ΡŽΡ‡Π°Ρ‚Π΅Π»Π΅ΠΉ, Ρ„Π»Π°ΠΆΠΊΠΎΠ² ΠΈ ΠΏΠΎΠ»Π·ΡƒΠ½ΠΊΠΎΠ² с Ρ€Π°Π·Π½Ρ‹ΠΌΠΈ состояниями ΠΈ Ρ€Π°Π·ΠΌΠ΅Ρ€Π°ΠΌΠΈ.

Β 

ИспользованиС Π·Π½Π°Ρ‡Π΅Π½ΠΈΠΉ Environment Π² ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΡΠΊΠΎΠΌ стилС

Для ΠΌΠ½ΠΎΠ³ΠΈΡ… view Π²Π°ΠΆΠ½ΠΎ ΠΈΠΌΠ΅Ρ‚ΡŒ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡ‚ΡŒ Π°Π΄Π°ΠΏΡ‚ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ ΠΈΡ… ΠΊ Ρ€Π°Π·Π»ΠΈΡ‡Π½Ρ‹ΠΌ значСниям environment. НапримСр, элСмСнт управлСния Π΄ΠΎΠ»ΠΆΠ΅Π½ ΡƒΠΊΠ°Π·Ρ‹Π²Π°Ρ‚ΡŒ, ΠΊΠΎΠ³Π΄Π° ΠΎΠ½ ΠΎΡ‚ΠΊΠ»ΡŽΡ‡Π΅Π½. Для эффСкта подсвСтки для Π½Π°ΠΆΠΈΠΌΠ°Π΅ΠΌΠΎΠ³ΠΎ view ΠΌΠΎΠΆΠ΅Ρ‚ ΠΏΠΎΡ‚Ρ€Π΅Π±ΠΎΠ²Π°Ρ‚ΡŒΡΡ Π΄Ρ€ΡƒΠ³ΠΎΠΉ Ρ†Π²Π΅Ρ‚, Ссли цвСтовая схСма установлСна Π½Π° Ρ‚Ρ‘ΠΌΠ½ΡƒΡŽ, ΠΈΠ»ΠΈ Π°Π½ΠΈΠΌΠ°Ρ†ΠΈΡŽ измСнСния состояния, Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎ, придётся ΠΏΡ€ΠΎΠΏΡƒΡΡ‚ΠΈΡ‚ΡŒ, Ссли Π²ΠΊΠ»ΡŽΡ‡Π΅Π½ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ ΡƒΠΌΠ΅Π½ΡŒΡˆΠ΅Π½ΠΈΡ двиТСния.

Π’Π°ΠΊΠΆΠ΅ Π²Π°ΠΆΠ½ΠΎ ΠΈΠΌΠ΅Ρ‚ΡŒ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡ‚ΡŒ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ ΡΡ‚ΠΈΠ»ΡŒ ΠΎΡ‚Ρ‚Π΅Π½ΠΊΠ° ΠΈΠ»ΠΈ Ρ€Π°Π·ΠΌΠ΅Ρ€ элСмСнта управлСния, Ссли ΠΎΠ½ ΡƒΠΊΠ°Π·Π°Π½.

НапримСр, Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎ, элСмСнты управлСния Π² ΠΏΠΎΡ‚ΠΎΠΊΠ΅ Π°Π΄Π°ΠΏΡ‚Π°Ρ†ΠΈΠΈ Π΄ΠΎΠ»ΠΆΠ½Ρ‹ Π±Ρ‹Ρ‚ΡŒ Π½Π΅ΠΌΠ½ΠΎΠ³ΠΎ большС, Ρ‡Π΅ΠΌ Π² Π΄Ρ€ΡƒΠ³ΠΈΡ… мСстах прилоТСния. Если стили view Π½Π°ΡΡ‚Ρ€Π°ΠΈΠ²Π°ΡŽΡ‚ΡΡ, ΠΊΠΎΠ³Π΄Π° controlSize большой, ΠΌΡ‹ ΠΌΠΎΠ³Π»ΠΈ Π±Ρ‹ ΡƒΡΡ‚Π°Π½ΠΎΠ²ΠΈΡ‚ΡŒ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ environment controlSize для этой части прилоТСния ΠΈ ΠΈΠ·Π±Π΅ΠΆΠ°Ρ‚ΡŒ создания ΡΠΎΠ²Π΅Ρ€ΡˆΠ΅Π½Π½ΠΎ Π½ΠΎΠ²Ρ‹Ρ… стилСй ΠΈΠ»ΠΈ view, спСцифичных для этого ΠΏΠΎΡ‚ΠΎΠΊΠ° Π°Π΄Π°ΠΏΡ‚Π°Ρ†ΠΈΠΈ.

ИспользованиС Π·Π½Π°Ρ‡Π΅Π½ΠΈΠΉ environment Π² стилС для встроСнного view SwiftUI Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚ Ρ‚Π°ΠΊ, ΠΊΠ°ΠΊ ΠΌΡ‹ ΠΈ ΠΎΠΆΠΈΠ΄Π°Π»ΠΈ, Π½ΠΎ Ссли ΠΌΡ‹ ΠΏΠΎΠΏΡ€ΠΎΠ±ΡƒΠ΅ΠΌ Ρ‚ΠΎ ΠΆΠ΅ самоС Π² стилС для view, для ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ³ΠΎ ΠΌΡ‹ создали собствСнный ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ» стиля, это Π½Π΅ сработаСт Π΄ΠΎΠ»ΠΆΠ½Ρ‹ΠΌ ΠΎΠ±Ρ€Π°Π·ΠΎΠΌ:

struct CheckboxMultipleChoiceStyle: MultipleChoiceStyle {
Β  Β  @Environment(\.isEnabled) var isEnabled

Β  Β  func makeBody(configuration: Configuration) -> some View {
Β  Β  Β  Β  /* ... */
Β  Β  Β  Β  Β  Β  .saturation(isEnabled ? 1 : 0)
Β  Β  Β  Β  Β  Β  .brightness(isEnabled ? 0 : -0.2)
Β  Β  }
}

Β 

MultipleChoice(selection: $extras) {
Β  Β  ForEach(Extra.allCases) { extra in
Β  Β  Β  Β  Text(extra.name).tag(extra)
Β  Β  }
}
.disabled(isOutOfStock)
.multipleChoiceStyle(.checkbox)

Бписок Ρ„Π»Π°ΠΆΠΊΠΎΠ², Π³Π΄Π΅ ΠΎΡ‚ΠΌΠ΅Ρ‡Π΅Π½Ρ‹ Π΄Π²Π°, ΠΎΠ±ΠΎΠ·Π½Π°Ρ‡Π΅Π½Π½Ρ‹Ρ… Π±Π΅Π»ΠΎΠΉ Π³Π°Π»ΠΎΡ‡ΠΊΠΎΠΉ Π½Π° синСм Ρ„ΠΎΠ½Π΅.
Β Π’ΠΊΠ»ΡŽΡ‡Π΅Π½Ρ‹


Бписок ΠΎΡ‚ΠΊΠ»ΡŽΡ‡Π΅Π½Π½Ρ‹Ρ… Ρ„Π»Π°ΠΆΠΊΠΎΠ², Π³Π΄Π΅ ΠΎΡ‚ΠΌΠ΅Ρ‡Π΅Π½Ρ‹ Π΄Π²Π°, ΠΎΠ±ΠΎΠ·Π½Π°Ρ‡Π΅Π½Π½Ρ‹Ρ… Π±Π΅Π»ΠΎΠΉ Π³Π°Π»ΠΎΡ‡ΠΊΠΎΠΉ Π½Π° синСм Ρ„ΠΎΠ½Π΅.ΠžΡ‚ΠΊΠ»ΡŽΡ‡Π΅Π½Ρ‹, Π½ΠΎ выглядят Ρ‚Π°ΠΊ ΠΆΠ΅, ΠΊΠ°ΠΊ ΠΈ ΠΏΡ€ΠΈ Π²ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠΈ

РаньшС это Π½Π΅ΠΌΠ½ΠΎΠ³ΠΎ сбивало с Ρ‚ΠΎΠ»ΠΊΡƒ, ΠΏΠΎΡ‚ΠΎΠΌΡƒ Ρ‡Ρ‚ΠΎ Π½Π΅ Π±Ρ‹Π»ΠΎ ΠΎΡ‡Π΅Π²ΠΈΠ΄Π½ΠΎ, ΠΏΠΎΡ‡Π΅ΠΌΡƒ это Π½Π΅ Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚. Однако, начиная с Xcode 14.1, запуск этого ΠΊΠΎΠ΄Π° Π²Ρ‹Π·ΠΎΠ²Π΅Ρ‚ ΠΏΠΎΠ»Π΅Π·Π½ΠΎΠ΅ ΠΏΡ€Π΅Π΄ΡƒΠΏΡ€Π΅ΠΆΠ΄Π΅Π½ΠΈΠ΅ Π²ΠΎ врСмя выполнСния, ΠΎΠ±ΡŠΡΡΠ½ΡΡŽΡ‰Π΅Π΅ ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΡƒ:


Β 
CheckboxMultipleChoiceStyle Π½Π΅ являСтся View, поэтому, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ environment, Π½Π°ΠΌ Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ ΠΏΠΎΠΌΠ΅ΡΡ‚ΠΈΡ‚ΡŒ это Π²ΠΎ view ΠΈΠ»ΠΈ ΠΊΠ°ΠΊ-Ρ‚ΠΎ ΠΎΠ±Π½ΠΎΠ²ΠΈΡ‚ΡŒ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ environment Π² стилС.

Один ΠΈΠ· способов использования ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½ΠΎΠΉ environment ΠΈΠ· стиля β€” это ΠΎΠ±Π΅Ρ€Π½ΡƒΡ‚ΡŒ body нашСго стиля Π² Π½ΠΎΠ²ΠΎΠ΅ view:

struct CheckboxMultipleChoice: View {
Β  Β  var configuration: MultipleChoiceStyleConfiguration

Β  Β  @Environment(\.isEnabled) var isEnabled

Β  Β  var body: some View {
Β  Β  Β  Β  /* ... */
Β  Β  Β  Β  Β  Β  .saturation(isEnabled ? 1 : 0)
Β  Β  Β  Β  Β  Β  .brightness(isEnabled ? 0 : -0.2)
Β  Β  }
}

struct CheckboxMultipleChoiceStyle: MultipleChoiceStyle {
Β  Β  func makeBody(configuration: Configuration) -> some View {
Β  Β  Β  Β  CheckboxMultipleChoice(configuration: configuration)
Β  Β  }
}

Бписок Ρ„Π»Π°ΠΆΠΊΠΎΠ², Π³Π΄Π΅ ΠΎΡ‚ΠΌΠ΅Ρ‡Π΅Π½Ρ‹ Π΄Π²Π°, ΠΎΠ±ΠΎΠ·Π½Π°Ρ‡Π΅Π½Π½Ρ‹Ρ… Π±Π΅Π»ΠΎΠΉ Π³Π°Π»ΠΎΡ‡ΠΊΠΎΠΉ Π½Π° синСм Ρ„ΠΎΠ½Π΅.Π’ΠΊΠ»ΡŽΡ‡Π΅Π½Ρ‹
Бписок ΠΎΡ‚ΠΊΠ»ΡŽΡ‡Π΅Π½Π½Ρ‹Ρ… Ρ„Π»Π°ΠΆΠΊΠΎΠ², Π³Π΄Π΅ ΠΎΡ‚ΠΌΠ΅Ρ‡Π΅Π½Ρ‹ Π΄Π²Π°, ΠΎΠ±ΠΎΠ·Π½Π°Ρ‡Π΅Π½Π½Ρ‹Π΅ Π±Π΅Π»ΠΎΠΉ Π³Π°Π»ΠΎΡ‡ΠΊΠΎΠΉ Π½Π° сСром Ρ„ΠΎΠ½Π΅.Π’Ρ‹ΠΊΠ»ΡŽΡ‡Π΅Π½Ρ‹

Π₯отя Ρ‚Π΅Ρ…Π½ΠΈΠΊΠ°, описанная Π²Ρ‹ΡˆΠ΅, Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚, ΠΊ соТалСнию, Π½Π°ΠΌ Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ ΠΎΡ‚Π½ΠΎΡΠΈΡ‚ΡŒΡΡ ΠΊ стилям для ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΡΠΊΠΈΡ… view ΠΈΠ½Π°Ρ‡Π΅, Ρ‡Π΅ΠΌ ΠΊ встроСнным стилям view, ΠΈ Π½Π΅ Π·Π°Π±Ρ‹Π²Π°Ρ‚ΡŒ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ эту Ρ‚Π΅Ρ…Π½ΠΈΠΊΡƒ ΠΊΠ°ΠΆΠ΄Ρ‹ΠΉ Ρ€Π°Π·, ΠΊΠΎΠ³Π΄Π° ΠΌΡ‹ создаСм ΡΡ‚ΠΈΠ»ΡŒ для ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΡΠΊΠΎΠ³ΠΎ view.

Β 

Dynamic Property (динамичСскоС свойство)

ΠœΡ‹ Π±Ρ‹Π»ΠΈ Π½Π΅ СдинствСнными, ΠΊΡ‚ΠΎ Ρ‚Π°ΠΊ Π΄ΡƒΠΌΠ°Π»: Π½Π° SwiftUI Digital Lounges WWDC 2022 ΠΊΡ‚ΠΎ-Ρ‚ΠΎ спросил ΠΎΠ± этом ΠΈ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΠ» ΠΎΡ‚Π²Π΅Ρ‚, Ρ‡Ρ‚ΠΎ SwiftUI обновляСт значСния срСды для Ρ‡Π»Π΅Π½ΠΎΠ², ΡΠΎΠΎΡ‚Π²Π΅Ρ‚ΡΡ‚Π²ΡƒΡŽΡ‰ΠΈΡ… DynamicProperty, ΠΈ Π΄Π΅Π»Π°Π΅Ρ‚ это рСкурсивно.

ДокумСнтация для DynamicProperty Π½Π΅ содСрТит подробностСй, Π½ΠΎ ΡƒΠΏΠΎΠΌΠΈΠ½Π°Π΅Ρ‚ ΡΠ»Π΅Π΄ΡƒΡŽΡ‰Π΅Π΅:

β€œView присваиваСт значСния этим свойствам Π΄ΠΎ пСрСсчёта view’s body”.

Π’Π΅ΠΏΠ΅Ρ€ΡŒ Π½Π΅ сказано, ΠΊΠ°ΠΊΠΈΠ΅ значСния, Π½ΠΎ ΠΏΠΎΡ…ΠΎΠΆΠ΅, Ρ‡Ρ‚ΠΎ это ΠΌΠΎΠ³ΡƒΡ‚ Π±Ρ‹Ρ‚ΡŒ значСния, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅, ΠΊΠ°ΠΊ ΠΌΡ‹ ΠΏΠΎΠ»Π°Π³Π°Π΅ΠΌ, Π΄ΠΎΠ»ΠΆΠ½Ρ‹ Ρ€Π°Π±ΠΎΡ‚Π°Ρ‚ΡŒ с @State ΠΈΠ»ΠΈ @Environment.

Π’Π°ΠΊ ΠΌΠΎΠΆΠ΅ΠΌ Π»ΠΈ ΠΌΡ‹ просто ΡΠΎΠ³Π»Π°ΡΠΎΠ²Π°Ρ‚ΡŒ наш ΡΡ‚ΠΈΠ»ΡŒ с DynamicProperty?

protocol RangeSliderStyle: DynamicProperty {
Β  Β  // ...
}

Β 

Пока ΡΡ‚ΠΈΠ»ΡŒ находится Π²ΠΎ view, Π° ΠΎΠ±ΠΎΠ»ΠΎΡ‡ΠΊΠ° свойства @Environment Ρ‚Π°ΠΊΠΆΠ΅ соотвСтствуСт DynamicProperty, это Π½Π΅ Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚.

ВмСсто этого ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΠΏΠΎΠΏΡ‹Ρ‚Π°Ρ‚ΡŒΡΡ ΠΏΡ€ΠΈΠΌΠ΅Π½ΠΈΡ‚ΡŒ ΡΡ‚ΠΈΠ»ΡŒ ΠΊ ΠΏΡ€ΠΎΠΌΠ΅ΠΆΡƒΡ‚ΠΎΡ‡Π½ΠΎΠΌΡƒ view:

struct ResolvedRangeSliderStyle: View {
Β  Β  var configuration: RangeSliderStyleConfiguration

Β  Β  var style: any RangeSliderStyle

Β  Β  var body: some View {
Β  Β  Β  Β  AnyView(style.makeBody(configuration: configuration))
Β  Β  }
}

Β 

Π—Π°Ρ‚Π΅ΠΌ ΠΌΡ‹ ΠΏΠ΅Ρ€Π΅Π΄Π°Π΅ΠΌ ΡΡ‚ΠΈΠ»ΡŒ ΠΏΡ€ΠΎΠΌΠ΅ΠΆΡƒΡ‚ΠΎΡ‡Π½ΠΎΠΌΡƒ view Π² нашСм ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Π΅:

var body: some View {
Β  Β  let configuration = RangeSliderStyleConfiguration(range: $range, bounds: bounds, label: .init(label))
Β  Β  ResolvedRangeSliderStyle(configuration: configuration, style: style)
}

Β 

Но ΠΏΠΎΠΏΡ€ΠΎΠ±ΠΎΠ²Π°Π² это, ΠΌΡ‹ Π²ΠΈΠ΄ΠΈΠΌ, Ρ‡Ρ‚ΠΎ это всё Π΅Ρ‰Ρ‘ Π½Π΅ Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚.

Π’ Π½Π°ΡˆΠΈΡ… экспСримСнтах ΠΌΡ‹ ΠΎΠ±Π½Π°Ρ€ΡƒΠΆΠΈΠ»ΠΈ, Ρ‡Ρ‚ΠΎ SwiftUI обновляСт значСния environment для стиля, Ссли Ρƒ нас Π΅ΡΡ‚ΡŒ ΠΊΠΎΠ½ΠΊΡ€Π΅Ρ‚Π½Ρ‹ΠΉ Ρ‚ΠΈΠΏ стиля Π² срСдС.

ΠŸΠΎΡΡ‚ΠΎΠΌΡƒ вмСсто этого ΠΌΡ‹ пишСм ΠΎΠ±Ρ‘Ρ€Ρ‚ΠΊΡƒ view, которая являСтся ΠΎΠ±Ρ‰Π΅ΠΉ для стиля:

struct ResolvedRangeSliderStyle: View {
Β  Β  var configuration: RangeSliderStyleConfiguration

Β  Β  var style: Style

Β  Β  var body: some View {
Β  Β  Β  Β  style.makeBody(configuration: configuration)
Β  Β  }
}

Β 

ΠŸΠΎΠ»Π·ΡƒΠ½ΠΎΠΊ Π΄ΠΈΠ°ΠΏΠ°Π·ΠΎΠ½Π° с синим Ρ†Π²Π΅Ρ‚ΠΎΠΌ Π·Π°Π»ΠΈΠ²ΠΊΠΈ.Β Π­Ρ‚ΠΎΡ‚ ΠΏΠΎΠ»Π·ΡƒΠ½ΠΎΠΊ Π΄ΠΈΠ°ΠΏΠ°Π·ΠΎΠ½Π° Π΄ΠΎΠ»ΠΆΠ΅Π½ Π²Ρ‹Π³Π»ΡΠ΄Π΅Ρ‚ΡŒ ΠΎΡ‚ΠΊΠ»ΡŽΡ‡Π΅Π½Π½Ρ‹ΠΌ.

Однако Ρ‚Π΅ΠΏΠ΅Ρ€ΡŒ, Ссли ΠΌΡ‹ попытаСмся ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‚ΡŒ ΡΡ‚ΠΈΠ»ΡŒ ΠΈΠ· environment, ΠΌΡ‹ столкнёмся с этой ошибкой компилятора:


Β 
ΠœΠΎΠΆΠ΅Ρ‚ ΠΏΠΎΠΊΠ°Π·Π°Ρ‚ΡŒΡΡ, Ρ‡Ρ‚ΠΎ ΠΌΡ‹ застряли здСсь, Π½ΠΎ ΠΌΡ‹ ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Ρ‰Π°Π΅ΠΌ это Π² Ρ€Π°ΡΡˆΠΈΡ€Π΅Π½ΠΈΠ΅ ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ»Π° стилСй, ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ΅ Π΄Π°Ρ‘Ρ‚ Π½Π°ΠΌ доступ ΠΊ ΠΊΠΎΠ½ΠΊΡ€Π΅Ρ‚Π½ΠΎΠΌΡƒ Ρ‚ΠΈΠΏΡƒ Ρ‡Π΅Ρ€Π΅Π· self:

extension RangeSliderStyle {
Β  Β  func resolve(configuration: Configuration) -> some View {
Β  Β  Β  Β  ResolvedRangeSliderStyle(configuration: configuration, style: self)
Β  Β  }
}

Β 

Π’Π΅Ρ€Π½ΡƒΠ²ΡˆΠΈΡΡŒ Π² body нашСго ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Π°, ΠΌΡ‹ Ρ‚Π΅ΠΏΠ΅Ρ€ΡŒ ΠΌΠΎΠΆΠ΅ΠΌ вмСсто этого Π²Ρ‹Π·Π²Π°Ρ‚ΡŒ ΠΌΠ΅Ρ‚ΠΎΠ΄ resolve для стиля:

var body: some View {
Β  Β  let configuration = RangeSliderStyleConfiguration(range: $range, bounds: bounds, label: .init(label))
Β  Β  AnyView(style.resolve(configuration: configuration))
}

Β 

ΠŸΡ€ΠΈΠ²Π΅Π΄Ρ‘Π½Π½Ρ‹ΠΉ Π²Ρ‹ΡˆΠ΅ ΠΊΠΎΠ΄ снова компилируСтся. БоотвСтствуя ΡΡ‚ΠΈΠ»ΡŽ DynamicProperty ΠΈ помСщая ΠΊΠΎΠ½ΠΊΡ€Π΅Ρ‚Π½Ρ‹ΠΉ ΡΡ‚ΠΈΠ»ΡŒ Π² ΠΏΡ€ΠΎΠΌΠ΅ΠΆΡƒΡ‚ΠΎΡ‡Π½ΠΎΠ΅ view с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ ΠΏΠΎΠΌΠΎΡ‰Π½ΠΈΠΊΠ° Π² ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ»Π΅ стиля, стили Ρ‚Π΅ΠΏΠ΅Ρ€ΡŒ ΠΌΠΎΠ³ΡƒΡ‚ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ @Environment ΠΈ Π΄Ρ€ΡƒΠ³ΠΈΠ΅ ΠΎΠ±Ρ‘Ρ€Ρ‚ΠΊΠΈ свойств, ΡΠΎΠΎΡ‚Π²Π΅Ρ‚ΡΡ‚Π²ΡƒΡŽΡ‰ΠΈΠ΅ DynamicProperty, Π±Π΅Π· нСобходимости ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ ΠΎΠ±Ρ…ΠΎΠ΄Π½ΠΎΠΉ ΠΏΡƒΡ‚ΡŒ Π² ΠΊΠ°ΠΆΠ΄ΠΎΠΌ стилС.

ΠŸΠΎΠ»Π·ΡƒΠ½ΠΎΠΊ Π΄ΠΈΠ°ΠΏΠ°Π·ΠΎΠ½Π° с сСрым Ρ†Π²Π΅Ρ‚ΠΎΠΌ Π·Π°Π»ΠΈΠ²ΠΊΠΈ.
Π­Ρ‚ΠΎΡ‚ ΠΏΠΎΠ»Π·ΡƒΠ½ΠΎΠΊ Π΄ΠΈΠ°ΠΏΠ°Π·ΠΎΠ½Π°, Π½Π°ΠΊΠΎΠ½Π΅Ρ†, выглядит ΠΎΡ‚ΠΊΠ»ΡŽΡ‡Π΅Π½Π½Ρ‹ΠΌ.

Β 

Style Propagation

Одним ΠΈΠ· прСимущСств ΡΡ‚ΠΈΠ»ΡŒΠ½Ρ‹Ρ… view являСтся Ρ‚ΠΎ, Ρ‡Ρ‚ΠΎ Π½Π°ΠΌ Π½Π΅ трСбуСтся Π·Π°Π΄Π°Π²Π°Ρ‚ΡŒ ΡΡ‚ΠΈΠ»ΡŒ нСпосрСдствСнно для ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ view Π² нашСм ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠΈ. ВмСсто этого ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΡƒΡΡ‚Π°Π½ΠΎΠ²ΠΈΡ‚ΡŒ ΡΡ‚ΠΈΠ»ΡŒ Π²ΠΎ view ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€Π° для экрана ΠΈΠ»ΠΈ Π² ΠΊΠΎΡ€Π½Π΅ прилоТСния, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ этот ΡΡ‚ΠΈΠ»ΡŒ Π²ΠΎ всСм ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠΈ:

WindowGroup {
Β  Β  ContentView()
Β  Β  Β  Β  .buttonStyle(.borderedProminent)
Β  Β  Β  Β  .toggleStyle(.checkbox)
Β  Β  Β  Β  .rangeSliderStyle(.rounded)
Β  Β  Β  Β  // ...
}

Лист с ΠΊΠ½ΠΎΠΏΠΊΠΎΠΉ, ΠΏΠ΅Ρ€Π΅ΠΊΠ»ΡŽΡ‡Π°Ρ‚Π΅Π»Π΅ΠΌ ΠΈ ΠΏΠΎΠ»Π·ΡƒΠ½ΠΊΠΎΠΌ Π΄ΠΈΠ°ΠΏΠ°Π·ΠΎΠ½Π°, Π³Π΄Π΅ ΠΊΠ½ΠΎΠΏΠΊΠ° ΠΈ ΠΏΠ΅Ρ€Π΅ΠΊΠ»ΡŽΡ‡Π°Ρ‚Π΅Π»ΡŒ ΠΎΡ‚ΠΎΠ±Ρ€Π°ΠΆΠ°ΡŽΡ‚ΡΡ с ΠΈΡ… автоматичСскими стилями, Π° ΠΏΠΎΠ»Π·ΡƒΠ½ΠΎΠΊ Π΄ΠΈΠ°ΠΏΠ°Π·ΠΎΠ½Π° отобраТаСтся со скруглСнным стилСм.Β 

К соТалСнию, Π² Π½Π΅ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Ρ… случаях стили для встроСнных view SwiftUI, Ρ‚Π°ΠΊΠΈΠ΅ ΠΊΠ°ΠΊ ButtonStyle ΠΈ ToggleStyle, Π½Π΅ Ρ€Π°ΡΠΏΡ€ΠΎΡΡ‚Ρ€Π°Π½ΡΡŽΡ‚ΡΡ Π½Π° view, ΠΎΡ‚ΠΎΠ±Ρ€Π°ΠΆΠ°Π΅ΠΌΡ‹Π΅ Π² модальной ΠΏΡ€Π΅Π·Π΅Π½Ρ‚Π°Ρ†ΠΈΠΈ.

Π’Π°ΠΊΠΈΠΌ ΠΎΠ±Ρ€Π°Π·ΠΎΠΌ, Ссли ΠΌΡ‹ прСдставляСм view Π² модальной ΠΏΡ€Π΅Π·Π΅Π½Ρ‚Π°Ρ†ΠΈΠΈ, Ρ‚Π°ΠΊΠΎΠΉ ΠΊΠ°ΠΊ sheet, fullscreenCover ΠΈΠ»ΠΈ popover, Π½Π°ΠΌ Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ ΠΎΠ±ΡΠ·Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎ ΡΠ±Ρ€ΠΎΡΠΈΡ‚ΡŒ стили Π² прСдставлСнном view, Ссли ΠΌΡ‹ Ρ…ΠΎΡ‚ΠΈΠΌ, Ρ‡Ρ‚ΠΎΠ±Ρ‹ стили использовались ΠΈ Ρ‚Π°ΠΌ.

Π›ΡŽΠ±ΠΎΠΏΡ‹Ρ‚Π½ΠΎ, Ρ‡Ρ‚ΠΎ стили ΠΏΠ΅Ρ€Π΅Π΄Π°ΡŽΡ‚ΡΡ ΠΊΠΎΠ½Ρ‚Π΅Π½Ρ‚Ρƒ Π²ΠΎ Π²ΡΠΏΠ»Ρ‹Π²Π°ΡŽΡ‰ΠΈΡ… ΠΎΠΊΠ½Π°Ρ…, Ссли ΠΌΠΎΠ΄ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ находится Π²Π½ΡƒΡ‚Ρ€ΠΈ NavigationStack, Π° Ссли Ρƒ нас Π΅ΡΡ‚ΡŒ NavigationStack Π²Π½ΡƒΡ‚Ρ€ΠΈ TabView, стили ΠΏΠ΅Ρ€Π΅Π΄Π°ΡŽΡ‚ΡΡ всСм Ρ‚ΠΈΠΏΠ°ΠΌ ΠΌΠΎΠ΄Π°Π»ΡŒΠ½Ρ‹Ρ… ΠΏΡ€Π΅Π·Π΅Π½Ρ‚Π°Ρ†ΠΈΠΉ.

Π—Π°ΠΌΠ΅Ρ‚ΠΊΠ°Β 
ЗначСния environment Ρ‚Π°ΠΊΠΆΠ΅ Π½Π΅ Ρ€Π°ΡΠΏΡ€ΠΎΡΡ‚Ρ€Π°Π½ΡΡŽΡ‚ΡΡ Π½Π° view Π² ΠΌΠΎΠ΄Π°Π»ΡŒΠ½Ρ‹Ρ… прСзСнтациях Π² iOS 13 ΠΈ ΡƒΠ΄Π°Π»ΡΡŽΡ‚ΡΡ Π²ΠΎ врСмя ΠΈΠ½Ρ‚Π΅Ρ€Π°ΠΊΡ‚ΠΈΠ²Π½ΠΎΠ³ΠΎ закрытия Π² iOS 14.

Π‘ΡƒΠ΄Π΅ΠΌ Π½Π°Π΄Π΅ΡΡ‚ΡŒΡΡ, Ρ‡Ρ‚ΠΎ это Π±ΡƒΠ΄Π΅Ρ‚ исправлСно Π² Π±ΡƒΠ΄ΡƒΡ‰ΠΈΡ… вСрсиях SwiftUI, Π½ΠΎ ΠΏΠΎΠΊΠ° Π΅ΡΡ‚ΡŒ способ Π·Π°ΡΡ‚Π°Π²ΠΈΡ‚ΡŒ это Ρ€Π°Π±ΠΎΡ‚Π°Ρ‚ΡŒ.

Если ΠΌΡ‹ Π½Π΅ΠΌΠ½ΠΎΠ³ΠΎ ΠΏΠΎΡ‚Π°Π½Ρ†ΡƒΠ΅ΠΌ с UIKit, ΠΎΠ±Π΅Ρ€Π½ΡƒΠ² view с ΠΌΠΎΠ΄ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ΠΎΠΌ sheet Π² UIViewControllerRepresentable, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ, Π² свою ΠΎΡ‡Π΅Ρ€Π΅Π΄ΡŒ, ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ UIHostingController для отобраТСния этого view, ΠΌΡ‹ смоТСм ΠΏΡ€Π΅Π΄ΡΡ‚Π°Π²ΠΈΡ‚ΡŒ sheets ΠΈ Ρ€Π°ΡΠΏΡ€ΠΎΡΡ‚Ρ€Π°Π½ΠΈΡ‚ΡŒ стили:

struct UIKitDanceView: UIViewControllerRepresentable {
Β  Β  var content: Content

Β  Β  init(@ViewBuilder content: @escaping () -> Content) {
Β  Β  Β  Β  self.content = content()
Β  Β  }

Β  Β  func makeUIViewController(context: Context) -> UIHostingController {
Β  Β  Β  Β  return UIHostingController(rootView: content)
Β  Β  }

Β  Β  func updateUIViewController(_ uiViewController: UIHostingController, context: Context) {

Β  Β  }
}

Β 

extension View {
Β  Β  func preserveStylesInSheets() -> some View {
Β  Β  Β  Β  UIKitDanceView {
Β  Β  Β  Β  Β  Β  Self
Β  Β  Β  Β  }
Β  Β  }
}

Β 

Π—Π°Ρ‚Π΅ΠΌ ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΠΏΡ€ΠΈΠΌΠ΅Π½ΠΈΡ‚ΡŒ всС наши стили Π² ΠΊΠΎΡ€Π½Π΅ нашСго прилоТСния ΠΈ Π΄ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ ΠΌΠΎΠ΄ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ preserveStylesInSheets(), Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΡƒΠ±Π΅Π΄ΠΈΡ‚ΡŒΡΡ, Ρ‡Ρ‚ΠΎ эти стили Ρ‚Π°ΠΊΠΆΠ΅ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡŽΡ‚ΡΡ Π² Π»ΡŽΠ±Ρ‹Ρ… ΠΌΠΎΠ΄Π°Π»ΡŒΠ½Ρ‹Ρ… прСзСнтациях.

Π—Π°ΠΌΠ΅Ρ‚ΠΊΠ°Β 
Π­Ρ‚ΠΎΡ‚ Ρ‚Ρ€ΡŽΠΊ Π½Π΅ Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚ Π² iOS 13, Π½ΠΎ Ρ‚Π°ΠΌ Π½Π°ΠΌ Ρ‚Π°ΠΊΠΆΠ΅ Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ ΡƒΡ‡ΠΈΡ‚Ρ‹Π²Π°Ρ‚ΡŒ значСния environment, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ Π½Π΅ Ρ€Π°ΡΠΏΡ€ΠΎΡΡ‚Ρ€Π°Π½ΡΡŽΡ‚ΡΡ Π½Π° view Π² ΠΌΠΎΠ΄Π°Π»ΡŒΠ½Ρ‹Ρ… прСзСнтациях.

WindowGroup {
Β  Β  ContentView()
Β  Β  Β  Β  .preserveStylesInSheets()
Β  Β  Β  Β  .buttonStyle(.borderedProminent)
Β  Β  Β  Β  .toggleStyle(.checkbox)
Β  Β  Β  Β  .rangeSliderStyle(.rounded)
Β  Β  Β  Β  // ...
}

Лист с ΠΊΠ½ΠΎΠΏΠΊΠΎΠΉ, ΠΏΠ΅Ρ€Π΅ΠΊΠ»ΡŽΡ‡Π°Ρ‚Π΅Π»Π΅ΠΌ ΠΈ ΠΏΠΎΠ»Π·ΡƒΠ½ΠΊΠΎΠΌ Π΄ΠΈΠ°ΠΏΠ°Π·ΠΎΠ½Π°, Π³Π΄Π΅ ΠΊΠ½ΠΎΠΏΠΊΠ° отобраТаСтся с Π·Π°ΠΌΠ΅Ρ‚Π½ΠΎΠΉ ΠΎΠΊΠ°Π½Ρ‚ΠΎΠ²ΠΊΠΎΠΉ, ΠΏΠ΅Ρ€Π΅ΠΊΠ»ΡŽΡ‡Π°Ρ‚Π΅Π»ΡŒ β€” с Ρ„Π»Π°ΠΆΠΊΠΎΠΌ, Π° ΠΏΠΎΠ»Π·ΡƒΠ½ΠΎΠΊ Π΄ΠΈΠ°ΠΏΠ°Π·ΠΎΠ½Π° β€” с Π·Π°ΠΊΡ€ΡƒΠ³Π»Π΅Π½Π½Ρ‹ΠΌ стилСм.

Β 

Β 

ПодвСдСниС ΠΈΡ‚ΠΎΠ³ΠΎΠ²

Π‘ ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ΠΌ, созданным с использованиСм стилСвых ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ΠΎΠ², ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΠΏΠ΅Ρ€Π΅ΠΌΠ΅ΡΡ‚ΠΈΡ‚ΡŒ ΠΌΠΎΠ΄ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€Ρ‹ стиля ΠΈΠ· Π½Π°ΡˆΠΈΡ… view Π² стили ΠΈ ΠΈΠΌΠ΅Ρ‚ΡŒ Ρ‡Ρ‘Ρ‚ΠΊΠΎΠ΅ Ρ€Π°Π·Π΄Π΅Π»Π΅Π½ΠΈΠ΅ ΠΌΠ΅ΠΆΠ΄Ρƒ стилСм ΠΈ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΎΠ½Π°Π»ΡŒΠ½ΠΎΡΡ‚ΡŒΡŽ прилоТСния.

Π’ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Π΅ view становятся Π±ΠΎΠ»Π΅Π΅ Π»Π°ΠΊΠΎΠ½ΠΈΡ‡Π½Ρ‹ΠΌΠΈ ΠΈ простыми Π² обслуТивании, ΠΈ ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ Π±ΠΎΠ»Π΅Π΅ эффСктивно Ρ€Π°Π±ΠΎΡ‚Π°Ρ‚ΡŒ со стилСм нашСго прилоТСния, ΠΊΠΎΠ³Π΄Π° ΠΎΠ½ Π½Π΅ Ρ‚Π°ΠΊ сильно пСрСплСтаСтся с Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΎΠ½Π°Π»ΡŒΠ½ΠΎΡΡ‚ΡŒΡŽ:

@main
struct MyApp: App {
Β  Β  var body: some Scene {
Β  Β  Β  Β  WindowGroup {
Β  Β  Β  Β  Β  Β  ContentView()
Β  Β  Β  Β  Β  Β  Β  Β  .theme(.springSummer2023)
Β  Β  Β  Β  }
Β  Β  }
}


Π Π°Π·ΠΌΠ΅Ρ‰Π΅Π½ΠΈΠ΅ всСх Π½Π°ΡˆΠΈΡ… ΠΌΠΎΠ΄ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ΠΎΠ² стиля view Π² ΡƒΠ΄ΠΎΠ±Π½ΠΎΠΉ theme(_:), ΠΏΠΎΠ΄ΠΎΠ±Π½ΠΎΠ΅ этому, Ρ‚Π°ΠΊΠΆΠ΅ ΡƒΠ΄ΠΎΠ±Π½ΠΎ ΠΏΡ€ΠΈ использовании ΠΏΡ€Π΅Π΄Π²Π°Ρ€ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹Ρ… просмотров Xcode для Ρ€Π°Π±ΠΎΡ‚Ρ‹ с экраном ΠΈΠ»ΠΈ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠ΅ΠΉ.

Π’ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡ‚ΡŒ ΠΏΠΈΡΠ°Ρ‚ΡŒ view, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΌΠΎΠΆΠ½ΠΎ ΡΡ‚ΠΈΠ»ΠΈΠ·ΠΎΠ²Π°Ρ‚ΡŒ ΠΏΠΎΡΠ»Π΅Π΄ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎ, ΠΈΠΌΠ΅Π΅Ρ‚ Ρ€Π΅ΡˆΠ°ΡŽΡ‰Π΅Π΅ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ для ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΊΠΈ систСм проСктирования Π² ΠΌΠ°ΡΡˆΡ‚Π°Π±Π΅, ΠΈ ΠΌΡ‹ надССмся, Ρ‡Ρ‚ΠΎ Apple Π² Π±ΡƒΠ΄ΡƒΡ‰Π΅ΠΌ сдСлаСт большС встроСнных view SwiftUI стилизуСмыми.

Π’Ρ‹ Ρ‚Π°ΠΊΠΆΠ΅ ΠΌΠΎΠΆΠ΅Ρ‚Π΅ ΠΏΠΎΠΏΡ€ΠΎΠ±ΠΎΠ²Π°Ρ‚ΡŒ это сами, Π·Π°Π³Ρ€ΡƒΠ·ΠΈΠ² эту Β Xcode Playground Π½Π° нашСй страницС GitHub, которая ΠΈΠΌΠ΅Π΅Ρ‚ настраиваСмоС view с API стилСй.
Π’ ΠΏΠΎΡΠ»Π΅Π΄ΡƒΡŽΡ‰Π΅ΠΌ постС ΠΌΡ‹ рассмотрим нСсколько Π±ΠΎΠ»Π΅Π΅ ΠΏΡ€ΠΎΠ΄Π²ΠΈΠ½ΡƒΡ‚Ρ‹Ρ… Π²Π°Ρ€ΠΈΠ°Π½Ρ‚ΠΎΠ² использования. Если Π²Ρ‹ Ρ…ΠΎΡ‚ΠΈΡ‚Π΅ ΡΠ»Π΅Π΄ΠΈΡ‚ΡŒ Π·Π° нашСй Ρ€Π°Π±ΠΎΡ‚ΠΎΠΉ ΠΈ Π±Ρ‹Ρ‚ΡŒ Π² курсС Π±ΡƒΠ΄ΡƒΡ‰ΠΈΡ… ΠΏΡƒΠ±Π»ΠΈΠΊΠ°Ρ†ΠΈΠΉ, ΠΏΠΎΠ΄ΠΎΠ±Π½Ρ‹Ρ… этой, рассмотритС Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡ‚ΡŒ ΠΏΠΎΠ΄ΠΏΠΈΡΠ°Ρ‚ΡŒΡΡ Π½Π° нас Π² Mastodon ΠΈΠ»ΠΈ Twitter ΠΈΠ»ΠΈ ΠΏΡ€ΠΈΡΠΎΠ΅Π΄ΠΈΠ½ΠΈΡ‚ΡŒΡΡ ΠΊ Π½Π°ΡˆΠ΅ΠΌΡƒ списку рассылки.

Бпасибо Chris Eidhof ΠΈ Eric Horacek Π·Π° ΠΎΡ‚Π·Ρ‹Π²Ρ‹ ΠΎ Ρ‡Π΅Ρ€Π½ΠΎΠ²ΠΈΠΊΠ΅ этого поста ΠΈ Natalye Childress Π·Π° Ρ€Π΅Π΄Π°ΠΊΡ‚ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ этого поста.
Β 

ΠžΡ€ΠΈΠ³ΠΈΠ½Π°Π» ΡΡ‚Π°Ρ‚ΡŒΠΈ


ΠžΡ†Π΅Π½ΠΈΡ‚Π΅ ΡΡ‚Π°Ρ‚ΡŒΡŽ
0
0
0
0
0

Π§Ρ‚ΠΎΠ±Ρ‹ Π΄ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΉ, Π°Π²Ρ‚ΠΎΡ€ΠΈΠ·ΡƒΠΉΡ‚Π΅ΡΡŒ
Π’ΠΎΠΉΡ‚ΠΈ
Π‘Π΅Π·ΡƒΠΌΠΎΠ²Π° Π’ΠΈΠΎΠ»Π°
ΠŸΠΈΡˆΠ΅Ρ‚ ΠΈ ΠΏΠ΅Ρ€Π΅Π²ΠΎΠ΄ΠΈΡ‚ ΡΡ‚Π°Ρ‚ΡŒΠΈ для SwiftBook