Содержание
В iOS 14 и SwiftUI 2 добавили модификатор
VStack {
Label("Swift Playground", systemImage: "swift")
Label("Swift Playground", systemImage: "swift")
.redacted(reason: .placeholder)
}

Когда пригодится прототип:
1. Показать вью, контент которой будет доступен после загрузки.
2. Показать недоступное или частично доступное содержимое.
3. Использовать вместо
Давайте рассмотрим сложный пример:
struct Device {
let name: String
let systemIcon: String
let description: String
}
extension Device {
static let airTag: Self =
.init(
name: "AirTag",
systemIcon: "airtag",
description: "Суперлёгкий способ находить свои вещи. Прикрепите один трекер AirTag к ключам, а другой — к рюкзаку. И теперь их видно на карте в приложении «Локатор»."
)
}
У модели есть название, системная иконка и описание. Я вынес
struct DeviceView: View {
let device: Device
var body: some View {
VStack(spacing: 20) {
HStack {
Image(systemName: device.systemIcon)
.resizable()
.frame(width: 42, height: 42)
Text(device.name)
.font(.title2)
}
VStack {
Text(device.description)
.font(.footnote)
Button("Перейти к покупке") {}
.buttonStyle(.bordered)
.padding(.vertical)
}
}
.padding(.horizontal)
}
}
Добавляем
struct ContentView: View {
var body: some View {
DeviceView(device: .airTag)
.redacted(reason: .placeholder)
}
}

Слева вью без модификатора. Справа — с ним. Давайте для наглядности добавим переключатель:
struct ContentView: View {
@State private var toggleRedacted: Bool = false
var body: some View {
VStack {
DeviceView(device: .airTag)
.redacted(reason: toggleRedacted ? .placeholder : [])
Toggle("Toggle redacted", isOn: $toggleRedacted)
.padding()
}
}
}
Если вы хотите не скрывать контент, примените модификатор
VStack(spacing: 20) {
HStack {
Image(systemName: device.systemIcon)
.resizable()
.frame(width: 42, height: 42)
Text(device.name)
.font(.title2)
}
.unredacted()
VStack {
Text(device.description)
.font(.footnote)
// Какой-то код ниже

В примере иконка и название девайса не скрыты.
Кнопка остаётся кликабельной и работает даже после того, как применили модификатор:
VStack {
Text(device.description)
.font(.footnote)
Button("Перейти к покупке") {
print("Кнопка кликабельна!")
}
.buttonStyle(.bordered)
.padding(.vertical)
}
Поведением кнопки управляйте вручную, ниже покажу, как это сделать.
Apple спроектировала структуру RedactionReasons, которая отвечает за причину редактирования, применяемую ко вью.
Есть варианты
Как можно реализовать кастомную причину:
extension RedactionReasons {
static let name = RedactionReasons(rawValue: 1 << 20)
static let description = RedactionReasons(rawValue: 2 << 20)
}
Реализуем с помощью протокола
У окружения есть проперти
struct DeviceView: View {
let device: Device
@Environment(.redactionReasons) var reasons
var body: some View {
VStack(spacing: 20) {
HStack {
Image(systemName: device.systemIcon)
.resizable()
.frame(width: 42, height: 42)
Text(device.name)
.unredacted(when: !reasons.contains(.name))
.font(.title2)
}
VStack {
Text(device.description)
.unredacted(when: !reasons.contains(.description))
.font(.footnote)
Button("Перейти к покупке") {
print("Кнопка не кликабельна!")
}
.disabled(!reasons.isEmpty)
.buttonStyle(.bordered)
.padding(.vertical)
}
}
.padding(.horizontal)
}
}
Я добавил кастомный метод
extension View {
@ViewBuilder
func unredacted(when condition: Bool) -> some View {
switch condition {
case true: unredacted()
case false: redacted(reason: .placeholder)
}
}
}
Если переключить, кнопка станет некликабельной.

Начнём с реализации своих причин:
enum Reasons {
case blurred
case standart
case sensitiveData
}
Реализуем вью-модификаторы, подходящие под причины выше:
struct Blurred: ViewModifier {
func body(content: Content) -> some View {
content
.padding()
.blur(radius: 4)
.background(.thinMaterial, in: Capsule())
}
}
struct Standart: ViewModifier {
func body(content: Content) -> some View {
content
.padding()
}
}
struct SensitiveData: ViewModifier {
func body(content: Content) -> some View {
VStack {
Text("Are you over 18 years old?")
.bold()
content
.padding()
.frame(width: 160, height: 160)
.overlay(.black, in: RoundedRectangle(cornerRadius: 20))
}
}
}
Чтобы увидеть результат из модификаторов выше в live preview, нужен код:
struct Blurred_Previews: PreviewProvider {
static var previews: some View {
Text("Hello, world!")
.modifier(Blurred())
}
}

Я взял
struct RedactableModifier: ViewModifier {
let reason: Reasons?
init(with reason: Reasons) { self.reason = reason }
@ViewBuilder
func body(content: Content) -> some View {
switch reason {
case .blurred: content.modifier(Blurred())
case .standart: content.modifier(Standart())
case .sensitiveData: content.modifier(SensitiveData())
case nil: content
}
}
}
У структуры есть
Последний шаг — реализовать метод к протоколу
extension View {
func redacted(with reason: Reasons?) -> some View {
modifier(RedactableModifier(with: reason ?? .standart))
}
}
Я не стал делать отдельную вью, в которой буду вызывать модификаторы. Вместо этого поместил всё в live preview:
struct RedactableModifier_Previews: PreviewProvider {
static var previews: some View {
VStack(spacing: 30) {
Text("Usual content")
.redacted(with: nil)
Text("How are good your eyes?")
.redacted(with: .blurred)
Text("Sensitive data")
.redacted(with: .sensitiveData)
}
}
}
Результат:

Другие туториалы
Рассмотрим когда вызываются методы контроллера и что можно делать внутри них. Когда настраивать вьюхи и данные.
Как очистить данные для приложения Catalyst включая AppGroup, Realm и UserDefaults.
Как добавить альтернативные иконки для A/B тестов на странице приложения.
Знакомимся с модификатором
В telegram-канале приходят уведомления о новых туториалах. В чате для iOS разработчиков ответят на вопросы.