Pull to refresh

SwiftUI уроки (часть 3)

Reading time15 min
Views1.9K

Ссылка на часть 2

Создаем UI с помощью стеков

Стеки в SUI похожи на стеквью в UIKit. Если комбинировать горизонтальные и вертикальные стеки можно создать комплексный UI для приложения, который будет отлично адаптировать под различные размеры экранов и типы устройств. В UIKit основное средство создания UI — это auto layout который применяется для правильного отображения вьюшек на экране. Зачастую для начинающих разработчиков автолейаут кажется чем‑то очень сложным как для применения так и обучения, так что если вы уже переходите на SUI — есть хорошие новости, автолэйаут вам больше не потребуется. Вы будете оперировать такими компонентами как VStack, HStack, ZStack.

В этой части мы посмотрим на все варианты стэков и попробуем создать UI использующий сеточное отображение наших вьюшек. Как и в прошлый раз мы будем работать над демо проектом скриншот которого вы можете видеть ниже, во время этого урока вы научитесь применять стеки для построения UI который вам нужен.

Что такое VStack, HStack и ZStack 

SwiftUI предоставляет три различных типа Stack, чтобы объединить View в различных ориентациях. В зависимости от того, как вы собираетесь располагать View, вы можете использовать:

  • HStack — располагает View горизонтально

  • VStack — располагает View вертикально

  • ZStack — наложение одного View поверх другого

На рисунке ниже показано, как эти стеки могут использоваться для организации View.

VStack:

		VStack {
			RoundedRectangle(cornerRadius: 20)
				.foregroundStyle(.orange)
				.overlay {
					Text("1")
						.font(.system(size: 50))
				}
			RoundedRectangle(cornerRadius: 20)
				.foregroundStyle(.orange)
				.overlay {
					Text("2")
						.font(.system(size: 50))
				}
			RoundedRectangle(cornerRadius: 20)
				.foregroundStyle(.orange)
				.overlay {
					Text("3")
						.font(.system(size: 50))
				}
		}

HStack:

struct ContentView: View {
	var body: some View {
		HStack {
			RoundedRectangle(cornerRadius: 20)
				.foregroundStyle(.orange)
				.overlay {
					Text("1")
						.font(.system(size: 50))
				}
			RoundedRectangle(cornerRadius: 20)
				.foregroundStyle(.orange)
				.overlay {
					Text("2")
						.font(.system(size: 50))
				}
			RoundedRectangle(cornerRadius: 20)
				.foregroundStyle(.orange)
				.overlay {
					Text("3")
						.font(.system(size: 50))
				}
		}
	}
}

ZStack:

struct ContentView: View {
	var body: some View {
		ZStack {
			RoundedRectangle(cornerRadius: 20)
				.foregroundStyle(.orange)
				.overlay {
					Text("1")
						.font(.system(size: 50))
				}
			RoundedRectangle(cornerRadius: 20)
				.foregroundStyle(.orange)
				.overlay {
					Text("2")
						.font(.system(size: 50))
				}
			RoundedRectangle(cornerRadius: 20)
				.foregroundStyle(.orange)
				.overlay {
					Text("3")
						.font(.system(size: 50))
				}
		}
	}
}

Создаем новый проект для работы со стеками

Для начала давайте создадим новый проект для нашего демо приложения, вписываем имя приложения SwiftUIStacks, вписываем имя организации и выбираем интерфейс SwiftUI, все как и в предыдущих уроках.

Кликаем далее и выбираем место для нашего проекта на вашем Mac, как и ранее для вас загрузится сгенерированный файл ContentView.swift.

Используем VStack 

Мы собираемся собрать UI который был изображен на первом скриншоте этой статьи, но для начала давайте разобьем его на более маленькие части. Начнем мы с оглавления, как показано ниже:

Итак чтобы добиться похожего результата как у нас на скриншоте выше мы должны объединить два текстовых вью в одном вертикальном стеке, давайте это сделаем:

struct ContentView: View {
    var body: some View {
		VStack {
			Text("Выберите")
				.font(.system(.largeTitle, design: .rounded))
				.fontWeight(.black)
				.padding(.leading)
			Text("Ваш девайс")
				.font(.system(.largeTitle, design: .rounded))
				.fontWeight(.black)
				.padding(.leading)
		}
    }
}

Когда мы добавили в наш вертикальный стек эти текствью они отобразились по центру экрана, причем как по вертикали так и по горизонтали, давайте для начала подвинем наш текст так чтобы он отображался из одной воображаемой X координаты с помощью аргумента alignment

struct ContentView: View {
    var body: some View {
		VStack(alignment: .leading) {
			Text("Выберите")
				.font(.system(.largeTitle, design: .rounded))
				.fontWeight(.black)
				.padding(.leading)
			Text("Ваш девайс")
				.font(.system(.largeTitle, design: .rounded))
				.fontWeight(.black)
				.padding(.leading)
		}
    }
}

Кстати еще можно указать отступ элементов находящихся внутри VStack друг от друга, для этого вам потребуется аргумент spacing.

struct ContentView: View {
    var body: some View {
		VStack(alignment: .leading, spacing: 2) {
			Text("Выберите")
				.font(.system(.largeTitle, design: .rounded))
				.fontWeight(.black)
				.padding(.leading)
			Text("Ваш девайс")
				.font(.system(.largeTitle, design: .rounded))
				.fontWeight(.black)
				.padding(.leading)
		}
    }
}

Попробуйте поиграть с цифрой которую вы передаете в spacing и увидите разницу в отступах внутри вашего стека.

Используем HStack 

Итак, теперь мы будем делать карточки наших устройств, начнем мы с Apple Watch и iPhone, здесь в карточках текст размещается также в VStack, а вот карточки друг с другом находятся в одном HStack. Давайте начнем с карточки Apple Watch, где она будет размещаться? Так как она находится под нашим условным оглавлением, то получается эта карточка будет находится также внутри другого VStack, мы поместим оглавление внутрь еще одного VStack и добавим внутрь него карточку с Apple Watch, для того чтобы легко создать новый VStack кликните на стек содержащий наше оглавление и выберите Embed in VStack, это позволит обернуть стек в другой стек.

После этого мы получим следующий сгенерированный код

struct ContentView: View {
    var body: some View {
		VStack {
			VStack(alignment: .leading, spacing: 2) {
				Text("Выберите")
					.font(.system(.largeTitle, design: .rounded))
					.fontWeight(.black)
					.padding(.leading)
				Text("Ваш девайс")
					.font(.system(.largeTitle, design: .rounded))
					.fontWeight(.black)
					.padding(.leading)
			}
		}
    }
}

Извлечение View 

Прежде чем мы продолжим я бы хотел показать один трюк который позволит вам лучше организовывать ваш код. При построении более сложного пользовательского интерфейса, который включает несколько компонентов, код внутри ContentView в конечном итоге станет огромным блоком кода, который сложно рецензировать и отлаживать. Всегда полезно разбивать большие блоки кода на более мелкие блоки, чтобы код было легче читать и поддерживать.

В Xcode есть встроенная функция для рефакторинга кода SwiftUI. Кликните по VStack, который содержит текстовые вью. Выберите «Extract subview» для извлечения кода.

Xcode извлекает блок кода и создает структуру по умолчанию с именем ExtractedView. Переименуйте ExtractedView в HeaderView, чтобы дать ему более осмысленное имя.

В итоге у нас должен получиться такой фрагмент кода:

struct ContentView: View {
    var body: some View {
		VStack {
			HeaderView()
		}
    }
}


struct HeaderView: View {
	var body: some View {
		VStack(alignment: .leading, spacing: 2) {
			Text("Выберите")
				.font(.system(.largeTitle, design: .rounded))
				.fontWeight(.black)
				.padding(.leading)
			Text("Ваш девайс")
				.font(.system(.largeTitle, design: .rounded))
				.fontWeight(.black)
				.padding(.leading)
		}
	}
}

Интерфейс все еще остается тем же. Однако обратите внимание на блок кода в ContentView. Теперь он намного более чистый и легко читаемый.

Давайте продолжим реализацию интерфейса плиток девайсов. Сначала мы создадим интерфейс для плитки Apple Watch. Обновите ContentView следующим образом:

struct ContentView: View {
	var body: some View {
		VStack {
			HeaderView()
			VStack {
				VStack {
					Text("Apple Watch")
						.font(.system(.title, design: .rounded))
						.fontWeight(.black)
						.foregroundColor(.white)
					Text("$500")
						.font(.system(size: 40, weight: .heavy, design: .rounded))
						.foregroundColor(.white)
					Text("Series X")
						.font(.headline)
						.foregroundColor(.white)
				}
				.padding(40)
				.background(Color.gray)
				.cornerRadius(10)
			}
		}
	}
}

Здесь мы добавляем еще один VStack под HeaderView. Этот VStack используется для размещения трех текстовых видов для отображения плитки Apple Watch. Я не буду вдаваться в подробности padding, background и cornerRadius, потому что мы уже обсудили эти модификаторы в предыдущих статьях.

Далее мы собираемся реализовать интерфейс плитки IPhone. Эта плитка должна быть размещена прямо рядом с плиткой Apple Watch. Для этого вам нужно вложить VStack с Apple Watch в HStack. Кликните по VStack и выберите «Embed in HStack».

Xcode сгенерирует следующий код

struct ContentView: View {
	var body: some View {
		VStack {
			HeaderView()
			VStack {
				HStack {
					VStack {
						Text("Apple Watch")
							.font(.system(.title, design: .rounded))
							.fontWeight(.black)
							.foregroundColor(.white)
						Text("$500")
							.font(.system(size: 40, weight: .heavy, design: .rounded))
							.foregroundColor(.white)
						Text("Series X")
							.font(.headline)
							.foregroundColor(.white)
					}
					.padding(40)
					.background(Color.gray)
				.cornerRadius(10)
				}
			}
		}
	}
}

Теперь мы готовы создать интерфейс плитки iPhone. Код очень похож на код плитки Apple Watch, за исключением фона и цветов текста. Вставьте следующий код прямо под cornerRadius(10):

					VStack {
						Text("Iphone")
							.font(.system(.title, design: .rounded))
							.fontWeight(.black)
							.foregroundColor(.orange)
						Text("$999")
							.font(.system(size: 40, weight: .heavy, design: .rounded))
							.foregroundColor(.orange)
						Text("Series X")
							.font(.headline)
							.foregroundColor(.orange)
					}
					.padding(40)
					.background(Color.green)
					.cornerRadius(10)

Плитка получилась, но у нас на лицо очевидная проблема — они занимают пространство исходя из того как именно и какой в них расположен контент. Справиться с этой ситуацией поможет модификатор frame c помощью которого мы сможем сделать наши плитки одинакового размера, а соответственно и располагаться внутри стека они будут одинаково. Давайте попробуем внесли следующие изменения, добавим под VStack наших плиток следующую строку

.frame(minWidth: 0, maxWidth: .infinity, minHeight: 100, maxHeight: 140)

Обратите внимание на 28 и 45 строки. В данном случае мы установил фрейм плиток чтобы он рассчитывался исходя из следующих параметров, minWidth минимальная ширина равна нулю, но maxWidth максимальная может быть бесконечной как того потребует вью и ее контент, так как у нас два вью внутри одного HStack он в любом случае разместит их пропорционально друг другу, теперь по высоте, мы указали что минимальная высота которая нас устроит будет 100, а максимальная 140 — чтобы отобразить весь контент фрейм подстроился под значение равное 140 и в итоге мы получили две равные плитки.

Все выглядит уже неплохо, однако нам еще не хватает отступов по краям, ведь нашли плитки буквально облизывают края айфона, хотя этого мы не планировали, чтобы исправить это на помощь придет модификатор padding() добавьте следующий код под наш HStack.

.padding(.horizontal)

Padding создал необходимые отступы, при этом отступы только по горизонтали и теперь нашли плитки правильно удалены от краев, при этом как вы видите наш текст опять съехал, вообще для решения этой задачи есть множество подходов, мы можем сделать другие фреймы, мы можем сделать несколько строк для текста, можем уменьшит сам шрифт, но есть как мне кажется более подходящий и универсальный способ — использовать модификатор minimumScaleFactor который Устанавливает минимальное количество размера текста в представлении, чтобы оно вписывалось в доступное пространство. (Таким образом операционная система сама за вас посчитает как именно нужно уместить текст в той вью в которую вы пытаетесь ее вписать).

Добавьте к тексту который не поместился на экране следующий фрагмент кода:

.minimumScaleFactor(0.5)

Организация Кода

Прежде чем продолжать давайте сначала оптимизируем текущий код, чтобы сделать его более организованным. Если вы посмотрите на оба стэка, которые используются для отображения наших плиток, то заметите что код очень похож, за исключением следующих элементов:

  • название девайса,

  • его цена,

  • цвет текста,

  • фоновый цвет.

Чтобы упростить код и улучшить его повторное использование, мы можем выделить и сделать его адаптивным для различных девайсов которые мы заходим вписать туда позже.

Для этого нужно взять код блока VStack, содержащего информацию об Apple Watch, и извлечь его. Затем переименуем извлеченное представление в «DeviceView».

struct DeviceView: View {
	var body: some View {
		VStack {
			Text("Apple Watch")
				.font(.system(.title, design: .rounded))
				.fontWeight(.black)
				.foregroundColor(.white)
				.minimumScaleFactor(0.5)
			Text("$500")
				.font(.system(size: 40, weight: .heavy, design: .rounded))
				.foregroundColor(.white)
				.minimumScaleFactor(0.5)
			Text("Series X")
				.font(.headline)
				.foregroundColor(.white)
		}
		.frame(minWidth: 0, maxWidth: .infinity, minHeight: 100, maxHeight: 140)
		.padding(40)
		.background(Color.gray)
		.cornerRadius(10)
	}
}

И соответственно так

struct ContentView: View {
	var body: some View {
		VStack {
			HeaderView()
			VStack {
				HStack {
					DeviceView()

					VStack {
						Text("Iphone")
							.font(.system(.title, design: .rounded))
							.fontWeight(.black)
							.foregroundColor(.orange)
							.minimumScaleFactor(0.5)
						Text("$999")
							.font(.system(size: 40, weight: .heavy, design: .rounded))
							.foregroundColor(.orange)
							.minimumScaleFactor(0.5)
						Text("Series X")
							.font(.headline)
							.foregroundColor(.orange)
					}
					.frame(minWidth: 0, maxWidth: .infinity, minHeight: 100, maxHeight: 140)
					.padding(40)
					.background(Color.green)
					.cornerRadius(10)
				}
				.padding(.horizontal)
			}
		}
	}
}

Как мы говорили ранее у наших девайсов отличаются только некоторые параметры:

  • название девайса,

  • его цена,

  • цвет текста,

  • фоновый цвет.

Давайте добавим такие свойства в нашу вью и заменим их непосредственно там где они требуются следующим образом:

struct DeviceView: View {
	// Добавляем новые свойства нашей структуре
	var device: String
	var price: String
	var textColor: Color
	var bgColor: Color

	var body: some View {
		VStack {
			// Название девайса
			Text(device)
				.font(.system(.title, design: .rounded))
				.fontWeight(.black)
			// Цвет текста
				.foregroundColor(textColor)
				.minimumScaleFactor(0.5)
			// Цена девайса
			Text(price)
				.font(.system(size: 40, weight: .heavy, design: .rounded))
			// Цвет текста
				.foregroundColor(textColor)
				.minimumScaleFactor(0.5)
			Text("Series X")
				.font(.headline)
			// Цвет текста
				.foregroundColor(textColor)
		}
		.frame(minWidth: 0, maxWidth: .infinity, minHeight: 100, maxHeight: 140)
		.padding(40)
		// Цвет фона
		.background(bgColor)
		.cornerRadius(10)
	}
}

Разумеется после того как мы добавили эти свойства, у нас возникла ошибка которую нужно править

Давайте добавим параметры и их значения и посмотрим что из этого выйдет:

DeviceView(device: "Apple Watch", price: "500$", textColor: .white, bgColor: .gray)

Отлично теперь проделаем тоже самое со вторым девайсом, замените код на следующий.

struct ContentView: View {
	var body: some View {
		VStack {
			HeaderView()
			VStack {
				HStack {
					DeviceView(device: "Apple Watch", price: "500$", textColor: .white, bgColor: .gray)
					DeviceView(device: "Iphone", price: "999$", textColor: .orange, bgColor: .green)
				}
				.padding(.horizontal)
			}
		}
	}
}

Код внутри ContentView стал гораздо более организованным и понятым, при этом наш UI сохранился таким каким мы его и задавали.

Используем ZStack 

Отлично, мы создали плитки, но как вы помните нам еще нужно иметь дополнительную плашку которая как бы лежит на плитке с айфоном. Для этого нам пригодятся знания о ZStack, он позволяет располагать вьюшки друг на друге по оси z. Давайте встроим наш DeviceView с айфоном в ZStack и добавим следующий код:

struct ContentView: View {
	var body: some View {
		VStack {
			HeaderView()
			VStack {
				HStack {
					DeviceView(device: "Apple Watch", price: "500$", textColor: .white, bgColor: .gray)
					ZStack {
						DeviceView(device: "Iphone", price: "999$", textColor: .orange, bgColor: .green)

						Text("Лучшее для фотографов")
						.font(.system(.caption, design: .rounded))
						.fontWeight(.bold)
						.foregroundColor(.white)
						.padding(5)
						.background(.orange)
					}
				}
				.padding(.horizontal)
			}
		}
	}
}

Отлично, наша плашка появилась на плитке, но нам еще нужно разместить ее таким образом чтобы она была и на плитке, и на холсте под ней, давайте добьемся этого эффекта, в этом нам поможет модификатор .offset.

Добавьте еще один фрагмент кода прямо под бэкграундом для плашки 

.offset(x: 0, y: 110)

Положительное число в y параметре переместит вашу вью вниз, отрицательное наоборот вверх, попробуйте поиграть с оффсетом также и по позиции x, кстати попереключайте устройства в канвасе например на iPod touch или на iPad и посмотрите останется ли плашка на том же месте.

Применяя все полученные знания вы в принципе уже сможете сами добавить последнюю плитку которая должна расположиться снизу, будет здорово если вы вернетесь к первому скриншоту и попробуете воссоздать ее самостоятельно, если же не получилось — ничего страшного, давайте сделаем это вместе

Для начала давайте опять почистим наш код и уберем нашу плашку в отдельную структуру, наведите курсор на Text вью с надписью лучшее для фотографов и выберите extract subview. Новую структуру переименуйте в SmallLabelView. Должно получиться так:

struct SmallLabelView: View {
	var body: some View {
		Text("Лучшее для фотографов")
			.font(.system(.caption, design: .rounded))
			.fontWeight(.bold)
			.foregroundColor(.white)
			.padding(5)
			.background(.orange)
			.offset(x: 0, y: 110)
	}
}

struct ContentView: View {
	var body: some View {
		VStack {
			HeaderView()
			VStack {
				HStack {
					DeviceView(device: "Apple Watch", price: "500$", textColor: .white, bgColor: .gray)
					ZStack {
						DeviceView(device: "Iphone", price: "999$", textColor: .orange, bgColor: .green)
						SmallLabelView()
					}
				}
				.padding(.horizontal)
			}
		}
	}
}

Теперь давайте разместим еще одну плитку под нашими двумя уже готовыми, на первый взгляд у нас уже есть все готовое для того чтобы сделать плитку такую же как и сверху, но что делать с картинкой которая тут появилась, подхода в этом вопросе выделить можно два, либо мы мы меняем структуру DeviceView, либо пользуемся знаниями об офсетах, я выбираю второй вариант.

 struct ContentView: View {
	var body: some View {
		VStack {
			HeaderView()
			VStack(spacing: 20) {
				HStack {
					DeviceView(device: "Apple Watch", price: "500$", textColor: .white, bgColor: .gray)
					ZStack {
						DeviceView(device: "Iphone", price: "999$", textColor: .orange, bgColor: .green)
						SmallLabelView()
					}
				}
				.padding(.horizontal)
				ZStack {
					DeviceView(device: "MacBook", price: "1499$", textColor: .white, bgColor: .black)
					Image(systemName: "macbook")
						.font(.title)
						.foregroundStyle(.white)
						.offset(x: 0, y: -70)
				}
				.padding(.horizontal)
				
			}
		}
	}
}

Какие правки мы внесли, во первых мы добавили дополнительный spacing для VStack который разместил в себе HStack и нижнюю плитку, во вторых мы добавили ZStack под нашим HStack, он понадобился нам для того чтобы мы отобразили картинку макбука, мы разместили плитку с информацией о макбуке и наконец мы применили padding по горизонтали для всего ZStack чтобы отступы по бокам совпадали с теми что у нас заданы для плиток выше. Оказывается если переиспользовать уже заранее заготовленные вьюшки, то кодовая база становится меньше — запомните это!

Используем Spacer

Мы почти справились, однако как вы видите весь контент все еще отображается по центру, а оглавление тоже выглядит довольно странно — ведь оно должно начинаться от левого края. Для таких задач отлично подойдет такая вью как Spacer() этот самый спейсер заполняет собой все пустое пространство и как бы отодвигается собой все остальные вью которых он достает, давайте проверим как это работает отправив весь наш контент наверх с помощью Spacer()

struct ContentView: View {
	var body: some View {
		VStack {
			HeaderView()
			VStack(spacing: 20) {
				HStack {
					DeviceView(device: "Apple Watch", price: "500$", textColor: .white, bgColor: .gray)
					ZStack {
						DeviceView(device: "Iphone", price: "999$", textColor: .orange, bgColor: .green)
						SmallLabelView()
					}
				}
				.padding(.horizontal)
				ZStack {
					DeviceView(device: "MacBook", price: "1499$", textColor: .white, bgColor: .black)
					Image(systemName: "macbook")
						.font(.title)
						.foregroundStyle(.white)
						.offset(x: 0, y: -70)
				}
				.padding(.horizontal)
				
			}
		}
		Spacer()
	}
}

Обратите внимание что Spacer мы добавили под основным VStack который содержит весь контент который уже отображается на нашем экране, попробуйте переставить этот Spacer в разные места на экране и посмотрите какого можно добиться результата.

Итак следующая цель это перенести «Выберите Ваш девайс» к левому краю, здесь нам опять пригодятся знания о стэках, наше оглавление мы должны поместить в горизонтальный стек, а в правую часть этого стека положить Spacer который и отодвинет наше оглавление в бок, давайте это сделаем:

struct ContentView: View {
	var body: some View {
		VStack {
			HStack {
				HeaderView()
				Spacer()
			}
			VStack(spacing: 20) {
				HStack {
					DeviceView(device: "Apple Watch", price: "500$", textColor: .white, bgColor: .gray)
					ZStack {
						DeviceView(device: "Iphone", price: "999$", textColor: .orange, bgColor: .green)
						SmallLabelView()
					}
				}
				.padding(.horizontal)
				ZStack {
					DeviceView(device: "MacBook", price: "1499$", textColor: .white, bgColor: .black)
					Image(systemName: "macbook")
						.font(.title)
						.foregroundStyle(.white)
						.offset(x: 0, y: -70)
				}
				.padding(.horizontal)
			}
		}
		Spacer()
	}
}

Поздравляю, вы собрали свой первый комплексный экран! Впереди еще много более сложных экранов, но во всяком случае вы уже освоили стэки которые фактически являются одним из ваших основных инструментов. Но это еще не все, все таки я хочу показать вам второй вариант как мы могли бы обработать историю с появлением иконки в нашем DeviceView, давайте добавим туда еще одну иконку но ниже и пусть она будет опциональной.

Для начала давайте добавим новое свойство к нашему DeviceView

var infoIcon: Image?

Далее надо определиться где эта иконка будет, давайте разместим ее в правом нижнем углу и используем опциональную развертку, то есть если в infoIcon есть значение то показать иконку, если нет то и отображать ничего не надо.

Добавим следующий фрагмент кода под .foregroundColor(textColor) для самого нижнего текста в плитке:

			if infoIcon != nil {
				HStack {
					Spacer()
					infoIcon
						.foregroundStyle(.white)
						.offset(x: 30, y: 40)
				}
			}

Мы добавили еще один HStack в который уже слева получается положили Spacer который прижмет нашу иконку в право, далее мы можем буквально вызвать infoIcon как цельное вью, так как именно он выступает в качестве нашего опционального вью с картинкой и с помощью модификаторов добавить легкую корректировку связанную с цветом и точным местоположением где бы мы хотели увидеть эту иконку.

Теперь добавим иконку в инициализатор нашего DeviceView с макбуком

DeviceView(device: "MacBook", price: "1499$", textColor: .white, bgColor: .black, infoIcon: Image(systemName: "info.circle.fill"))

Весь измененный код должен выглядить следующим образом

struct DeviceView: View {
	// Добавляем новые свойства нашей структуре
	var device: String
	var price: String
	var textColor: Color
	var bgColor: Color
	var infoIcon: Image?

	var body: some View {
		VStack {
			// Название девайса
			Text(device)
				.font(.system(.title, design: .rounded))
				.fontWeight(.black)
			// Цвет текста
				.foregroundColor(textColor)
				.minimumScaleFactor(0.5)
			// Цена девайса
			Text(price)
				.font(.system(size: 40, weight: .heavy, design: .rounded))
			// Цвет текста
				.foregroundColor(textColor)
				.minimumScaleFactor(0.5)
			Text("Series X")
				.font(.headline)
			// Цвет текста
				.foregroundColor(textColor)
			if infoIcon != nil {
				HStack {
					Spacer()
					infoIcon
						.foregroundStyle(.white)
						.offset(x: 30, y: 40)
				}
			}

		}
		.frame(minWidth: 0, maxWidth: .infinity, minHeight: 100, maxHeight: 140)
		.padding(40)
		// Цвет фона
		.background(bgColor)
		.cornerRadius(10)
	}
}



struct ContentView: View {
	var body: some View {
		VStack {
			HStack {
				HeaderView()
				Spacer()
			}
			VStack(spacing: 20) {
				HStack {
					DeviceView(device: "Apple Watch", price: "500$", textColor: .white, bgColor: .gray)
					ZStack {
						DeviceView(device: "Iphone", price: "999$", textColor: .orange, bgColor: .green)
						SmallLabelView()
					}
				}
				.padding(.horizontal)
				ZStack {
					DeviceView(device: "MacBook", price: "1499$", textColor: .white, bgColor: .black, infoIcon: Image(systemName: "info.circle.fill"))
					Image(systemName: "macbook")
						.font(.title)
						.foregroundStyle(.white)
						.offset(x: 0, y: -70)
				}
				.padding(.horizontal)
			}
		}
		Spacer()
	}
}

И такой результат будет на экране нашего устройства

Поздравляю, на этом мы закончили знакомиться со стэками, надеюсь вам понравилось, но я бы порекомендовал потренироваться еще и поразмещать различные вью внутри стэков и стэки внутри стэков для закрепления материала.

Буду рад вашим комментариям и лайкам если статья оказалась полезной! Как и прежде подписывайтесь на мой телеграм-канал. В ближайшее время выйдут следующие части уроков по SwiftUI. 

Спасибо за прочтение!

Tags:
Hubs:
Total votes 4: ↑4 and ↓0+4
Comments1

Articles