Stacks and spacer
Скачать 4.8 Mb.
|
STACKS AND SPACER SHAPES AND STROKE Круг: Circle() .stroke(Color.black, lineWidth: 2) .frame(width: 44, height: 44) Ellipse: Ellipse() .stroke(Color.black, lineWidth: 2) .frame(width: 44, height: 88) Прямоугольник: Rectangle() .foregroundColor(.blue) .ignoresSafeArea() Прямоугольниксзакругленнымиуглами: RoundedRectangle(cornerRadius: 30, style: .continuous) .fill(Color.green) .frame(height: 44) .overlay(Text("Sign up").bold()) Капусула: Capsule() .fill(Color.green) .frame(height: 44) .overlay(Text("Sign up").bold()) ZStack { Rectangle() .fill(Color.blue).ignoresSafeArea() VStack { Circle() .stroke(Color.black, lineWidth: 2) .frame(width: 44, height: 44) Text("Meng To").bold() Capsule() .foregroundColor(Color.green) .frame(height: 44) .overlay(Text("Sign up")) } .padding() .background(Color.white) .clipShape(RoundedRectangle(cornerRadius: 25.0, style: .continuous)) .padding() } Боковая панель Боковая панель NavigationView { List { Label("Courses", systemImage: "book") Label("Tutorials", systemImage: "square") } .navigationTitle("Learn") } Начальный экран и заголовок Чтобы установить начальный экран, вам нужно объявить свой вид после списка. NavigationView { // List HomeView() } Заголовок навигации устанавливается внутри этого представления. struct HomeView: View { var body: some View { Text("Courses") .navigationTitle("Courses") } } Навигационная ссылка Чтобы разрешить навигацию, вам нужно установить NavigationLink для каждого элемента списка. NavigationLink(destination: HomeView()) { Label("Courses", systemImage: "book") } Панель инструментов(Toolbar) Navigation View Toolbar NavigationView { Text("My app") .toolbar { ToolbarItem { Image(systemName: "person.crop.circle") } } } ToolbarItem Placement (Размещение элемента панели инструментов) ToolbarItem(placement: .bottomBar) { Button(action: {}) { Image(systemName: "person.crop.circle") } } ToolbarItemGroup (Группа элементов панели инструментов) ToolbarItemGroup(placement: .confirmationAction) { Image(systemName: "person") Image(systemName: "ellipsis") } ToolbarItemGroup(placement: .bottomBar) { Image(systemName: "person") Spacer() Image(systemName: "ellipsis") Spacer() Image(systemName: "trash") } Окончательный код NavigationView { Text("My app") .toolbar { ToolbarItemGroup(placement: .bottomBar) { Image(systemName: "person") HStack { Image(systemName: "ellipsis") Divider() Image(systemName: "trash") .frame(width: 32, height: 32) .background(Color.blue) .mask(Circle()) } } } } Image View Изменение размера изображения Image("Illustration") .resizable() Соотношение сторон Image("Illustration") .resizable() .aspectRatio(contentMode: .fit) Выравнивание кадра Вы также можете выровнять изображение в пределах рамки. Image("Illustration") .resizable() .aspectRatio(contentMode: .fit) .frame(width: 200, height: 200, alignment: .center) Safe Area Layout Игнорировать безопасную зону Для пользовательского фона обычно применяется ignoreSafeArea. По умолчанию это будет применяться ко всем сторонам: верхней , нижней , ведущей , конечной , всем сразу. Color.blue.ignoresSafeArea() Края безопасной зоны Если вы хотите игнорировать только одно ребро, вы можете настроить значение ребра на .top, .bottom, .leading или .trailing. Color.blue.ignoresSafeArea(.all, edges: .top) Избегайте применения к контенту Safe Area очень помогает вам справляться с проблемами пользовательского интерфейса, которые возникают из-за выемки и пробелов индикатора дома. Рекомендуется избегать применения ignoreSafeArea к содержимому. Вместо этого вы должны отделить элементы, которые игнорируют безопасную область, например фоновый элемент. ZStack { Color.blue.ignoresSafeArea() VStack { Text("SwiftUI for iOS 14") .bold() } .frame(width: 300, height: 200) .background(Color.white) } Max Width and Frame Alignment Типичным способом выравнивания элементов является использование HStack + Spacer(). Однако для этого может потребоваться слишком много контейнеров, особенно если вы хотите выровнять по левому верхнему углу. Альтернативой является использование модификатора фрейма с maxWidth и выравниванием. Стеки и альтернативы разделителям(StacksandSpacerAlternatives) VStack { HStack { Image(systemName: "xmark") .frame(width: 32, height: 32) .background(Circle().stroke()) Spacer() } Spacer() } .padding() Максимальная ширина и выравнивание(MaxWidthandAlignment) Используя модификатор кадра с maxWidth и maxHeight , установленными на бесконечность , и выравниванием , установленным на topTrailing , вы получите тот же результат. Image(systemName: "xmark") .frame(width: 32, height: 32) .background(Circle().stroke()) .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topTrailing) .padding() Наложение(Overlay) Плавающую кнопку закрытия можно поместить в модификатор наложения, который помещает элемент над содержимым. Rectangle() .fill(Color.blue) .overlay( Image(systemName: "xmark") .frame(width: 32, height: 32) .background(Circle().stroke()) .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topTrailing) .padding() ) Тени и непрозрачность цвета Тени идеально подходят для добавления глубины пользовательскому интерфейсу. Когда ваш текст, кнопки и карточки слишком сильно сливаются с фоном, вы можете использовать тени, чтобы выделить их. Тень по умолчанию Использование модификатора тени со значением радиуса даст вам внешний вид тени iOS по умолчанию. Настройте радиус, чтобы сделать его меньше или больше. RoundedRectangle(cornerRadius: 25) .shadow(radius: 10) Пользовательская тень и непрозрачность Если вам нужны дополнительные параметры, вы можете изменить цвет, радиус, значения x и y. Один трюк, который я люблю использовать, это применение непрозрачности к полному цвету. Это даст вам максимальную гибкость для стилей и анимации. .shadow(color: Color.black.opacity(0.3), radius: 20, x: 0, y: 10) Множественные тени Чтобы добавить несколько теней, вы можете использовать несколько модификаторов теней. Убедитесь, что комбинированные тени не слишком сильные, с непрозрачностью менее 50%. Чем светлее ваш фон, тем ниже должен быть этот процент. Мне лично нравится, когда радиус удваивается по оси y. .shadow(color: Color.black.opacity(0.2), radius: 5, x: 0, y: 2) .shadow(color: Color.pink.opacity(0.3), radius: 20, x: 0, y: 10) Тень текста Text("App of the day") .font(.title).bold() .foregroundColor(.white) .shadow(radius: 20) Цветовая тень Используя технику «Цвет плюс непрозрачность», вы можете легко создавать тени, используя тот же цвет, что и цвет вашей карты. .shadow(color: Color.pink.opacity(0.3), radius: 20, x: 0, y: 10) Теперь у нас есть красивая карточка с хорошо контрастным текстом и свечением с помощью модификатора тени. VStack { Text("App of the day") .font(.title).bold() .foregroundColor(.white) .shadow(radius: 20) } .frame(width: 300, height: 400) .background(Color.pink) .cornerRadius(20) .shadow(color: Color.black.opacity(0.2), radius: 5, x: 0, y: 2) .shadow(color: Color.pink.opacity(0.3), radius: 20, x: 0, y: 10) Mask and Transparency (Маска и прозрачность) Transparency Mask Обрезка особенно хороша для маскировки контента, такого как текст и изображения. В этом примере и содержимое, и фон будут иметь непрозрачность 30%. VStack { Text("Mask and Transparency") .font(.title3).bold() } .padding() .background(Color.blue) .mask(Color.black.opacity(0.3)) Альфа-градиентная маска Отличная стратегия работы с контентом, который резко обрезается, — это применение непрозрачной градиентной маски. В этом примере мы маскируем содержимое, используя градиент сверху при непрозрачности 100%, вниз при непрозрачности 0%. .mask( LinearGradient(gradient: Gradient(colors: [Color.black, Color.black.opacity(0)]), startPoint: .top, endPoint: .bottom) ) Окончательный макет В этом примере макета мы применяем маску градиента к обрезанному содержимому, которое прекрасно адаптируется к фону. struct ContentView: View { var body: some View { ZStack { LinearGradient(gradient: Gradient(colors: [Color(#colorLiteral(red: 0.2196078449, green: 0.007843137719, blue: 0.8549019694, alpha: 1)), Color(#colorLiteral(red: 0.2588235438, green: 0.7568627596, blue: 0.9686274529, alpha: 1))]), startPoint: .topLeading, endPoint: .bottomTrailing) .ignoresSafeArea() VStack { ForEach(0 ..< 5) { item in Text("Mask and Transparency") .font(.title3).bold() .padding(.vertical) .frame(maxWidth: .infinity, alignment: .leading) Divider() } } .frame(height: 300, alignment: .top) .padding() .background(Color.white) .mask( LinearGradient(gradient: Gradient(colors: [Color.black, Color.black, Color.black, Color.black.opacity(0)]), startPoint: .top, endPoint: .bottom) ) .cornerRadius(30) .padding() } } } Clip Shape and Smooth Corners (Форма зажима и сглаженные углы) Карточка с закругленными углами Начнем с простого макета с картой и фоном. ZStack { Color.blue.ignoresSafeArea() VStack { Text("SwiftUI for iOS 14") .bold() } .frame(width: 300, height: 200) .background(Color.white) } Круг в форме зажима Вы можете использовать clipShape, чтобы замаскировать содержимое любой формы, например Circle или Capsule . .clipShape(Circle()) Стиль углового радиуса Непрерывный Чтобы применить непрерывные закругленные углы, которые повсеместно используются в iOS, используйте фигуру RoundedRectangle . .clipShape(RoundedRectangle(cornerRadius: 25, style: .continuous)) Окончательный код ZStack { Color.blue.ignoresSafeArea() VStack { Text("SwiftUI for iOS 14") .bold() } .frame(width: 300, height: 200) .background(Color.white) .clipShape(RoundedRectangle(cornerRadius: 25.0, style: .continuous)) } Tab View Pagination (Пагинация просмотра вкладок) Представление вкладок со стилем страницы TabView имеет модификатор tabViewStyle, который позволяет создавать горизонтальную прокрутку с разбиением на страницы. В этом макете у нас есть 3 представления. Каждый из этих RoundedRectangle может быть заменен выделенным представлением. TabView { RoundedRectangle(cornerRadius: 30).padding() RoundedRectangle(cornerRadius: 30).padding() RoundedRectangle(cornerRadius: 30).padding() } .tabViewStyle(PageTabViewStyle()) Показать или скрыть точки Вы можете установить indexDisplayMode для PageTabViewStyle на всегда, автоматически или никогда . .tabViewStyle(PageTabViewStyle(indexDisplayMode: .always)) TabView { RoundedRectangle(cornerRadius: 30) .fill(Color.blue) .padding() RoundedRectangle(cornerRadius: 30) .fill(Color.red) .padding() RoundedRectangle(cornerRadius: 30) .fill(Color.purple) .padding() } .tabViewStyle(PageTabViewStyle(indexDisplayMode: .always)) Lazy Stacks (Ленивые стеки) Лучшая производительность Ленивые стеки обеспечивают большую оптимизацию и лучший пользовательский интерфейс, поскольку элементы будут загружаться быстрее, только когда они видны. Для элементов, которые могут находиться за пределами экрана, следует использовать LazyVStack или LazyHStack вместо HStack и VStack. ЛенивыйVStack Чтобы проверить производительность, мы создаем 10 000 строк, расположенных вертикально, с помощью LazyVStack , встроенного в ScrollView. Вы заметите значительно лучшую производительность во время прокрутки и при начальной загрузке по сравнению с использованием VStack. ScrollView { LazyVStack(spacing: 16) { ForEach(0 ..< 10000) { item in RoundedRectangle(cornerRadius: 20) .fill(Color.white) .frame(height: 100) .shadow(radius: 100) } } .padding() } LazyVGrid Так же, как LazyVStack, вы можете использовать LazyVGrid для загрузки ваших элементов, только когда они находятся в поле зрения. ScrollView { LazyVGrid(columns: [GridItem(.adaptive(minimum: 100))]) { ForEach(0 ..< 10000) { item in RoundedRectangle(cornerRadius: 20) .fill(Color.white) .frame(height: 100) .shadow(radius: 100) } } .padding() } Вид навигации Оболочка представления навигации На корневом уровне вашей навигационной структуры вы должны обернуть все внутри навигационного представления. Это автоматически создаст панель навигации. Обратите внимание, что вы должны сделать это только один раз, включая все дочерние представления. NavigationView { ScrollView { RoundedRectangle(cornerRadius: 30) .frame(height: 1000) .padding() } } Название навигации Модификатор .navigationTitle следует применить к первой оболочке внутри NavigationView. Его не следует применять к самому NavigationView. В данном случае я обращаюсь к контейнеру ScrollView. NavigationView { ScrollView { // ... } .navigationTitle("Today") } Элементы панели навигации Вы можете добавить кнопки слева или справа от панели навигации с помощью модификатора navigationBarItems . .navigationBarItems(trailing: Image(systemName: "person.crop.circle")) НавигацияСсылка NavigationLink — это то, что позволяет вам перейти на другой экран. Это вызовет анимацию слайдов по умолчанию с помощью панели навигации. Кроме того, вы можете вернуться к предыдущему экрану. NavigationLink(destination: Text("New View")) { RoundedRectangle(cornerRadius: 30) .frame(height: 1000) .padding() } Link (Ссылка на сайт) Просмотр ссылки Используя объект Link, мы устанавливаем текст и пункт назначения, который имеет тип URL. Обратите внимание, что URL не может быть опцией, поэтому мы добавляем восклицательный знак в конце. Link("Design+Code", destination: URL(string: "https://designcode.io")!) Настроить ссылку Подобно тексту, вы можете настроить ссылку, используя модификаторы, такие как шрифт или цвет переднего плана. Link("Design+Code", destination: URL(string: "https://designcode.io")!) .font(.title) .foregroundColor(.purple) Значок ссылки Вы также можете обернуть ссылку вокруг любого элемента, например изображения. Link(destination: URL(string: "https://designcode.io")!) { Image(systemName: "link") .frame(width: 32, height: 32) .background(Color.blue) .mask(Circle()) .foregroundColor(.white) } Date Picker (Выбор даты) Выбор даты Сначала установите дату как состояние. Это средство выбора даты привязано к свойству selectedDate. Пользователи могут выбрать любую дату до текущей даты. struct ContentView: View { @State var selectedDate = Date() var body: some View { DatePicker("Date", selection: $selectedDate) } } Оболочка выбора даты Вы можете дополнительно настроить свой пользовательский интерфейс, обернув текст или изображение. DatePicker(selection: $selectedDate) { Text("Select a date") .font(.title3) } Диапазон дат ...Date() позволяет пользователю выбрать любую дату до сегодняшнего дня. В качестве альтернативы вы можете установить его начиная с текущей даты с помощью Date().... DatePicker("Range", selection: $selectedDate, in: ...Date()) Выбор времени Вместо конкретной даты вы можете разрешить пользователям выбирать часы и минуты. DatePicker("Time", selection: $selectedDate, displayedComponents: .hourAndMinute) Дата и время Вы можете комбинировать дату и час и минуту, используя массив. DatePicker("Date and time", selection: $selectedDate, displayedComponents: [.date, .hourAndMinute]) Дата и время сбора Мы можем комбинировать дату и время. DatePicker("Date and Time Picker", selection: $selectedDate, displayedComponents: [.date, .hourAndMinute]) Графический стиль выбора даты () GraphicalDatePickerStyle() позволяет сразу же отобразить полный пользовательский интерфейс календаря вместо отображения модального окна при нажатии. DatePicker("Select date", selection: $selectedDate) .datePickerStyle(GraphicalDatePickerStyle()) Статические данные Что такое статические данные? Статические данные — это образец набора данных, представленных в структурированном виде, которые вводятся вручную перед внедрением полного динамического решения. Модель данных Модель данных позволяет нам быстро ссылаться на значения внутри объекта. Он также содержит типы значений, что дает нам возможность соответствующим образом манипулировать данными. Например, у цвета совсем другие функции, чем у строки. struct Course: Identifiable { var id = UUID() var title: String var color: Color } Образец данных var courses = [ Course(title: "SwiftUI", color: Color.blue), Course(title: "UI Design", color: Color.red) ] Перебор данных Давайте сначала добавим представление, которое будет содержать все элементы курса. Внутри него мы будем перебирать данные, используя цикл ForEach. struct ContentView: View { var body: some View { List { ForEach(courses) { item in Text(item.title) .padding() .background(item.color) .cornerRadius(10) } } } } Закрыть пользовательское модальное окно Если вы хотите показать собственное модальное окно в своем приложении и не хотите использовать модификатор .sheet по умолчанию , вы можете легко создать прослушиватель onTapOutside , чтобы закрыть ваше модальное окно. Создайте состояние showModal Давайте начнем с создания нашего состояния showModal , которое будет переключаться между true и false. Начальное значение будет установлено на false, что означает, что модальное окно не будет отображаться по умолчанию. @State var showModal: Bool = false Создать ZStack Затем давайте создадим ZStack с выделенным серым цветом Rectangle() . Поверх прямоугольника покажем модальное окно. В моем случае я создал модальное окно в другом файле с именем SignInView и просто назвал его после Rectangle() . Добавьте .edgesIgnoringSafeArea(.all) , если вы хотите, чтобы прямоугольник проходил над безопасной областью экрана. ZStack { Rectangle() .foregroundColor(Color.black.opacity(0.5)) .edgesIgnoringSafeArea(.all) SignInView() } Добавьте жест onTapGesture Важная часть — добавить прослушиватель onTapGesture к прямоугольнику . Когда пользователь нажимает на нее (область за пределами модального окна), мы хотим закрыть модальное окно. Поэтому мы можем просто установить для переменной showModal значение false. .onTapGesture { showModal = false } Добавьте кнопку для отображения модального окна. Не забудьте добавить свою кнопку, чтобы переключить состояние showModal в true, чтобы вы могли показывать модальное окно, когда пользователь нажимает на него. Button(action: { showModal = true }) { Text("Sign in") } Окончательный код Наконец, оберните весь ZStack оператором if , чтобы мы показывали модальное окно и Rectangle() , когда для переменной showModal установлено значение true . Окончательный код будет выглядеть примерно так: Button(action: { showModal = true }) { Text("Sign in") } if showModal { ZStack { Rectangle() .foregroundColor(Color.black.opacity(0.5)) .edgesIgnoringSafeArea(.all) .onTapGesture { showModal = false } SigninView() } } Дата формата Как отформатировать дату как [день недели], [месяц] [день] , как на изображении ниже? Форматирование даты Apple создала класс DateFormatter , облегчающий процесс форматирования дат. Чтобы получить дату в виде строки и отобразить ее как вторник, 29 декабря, начните с создания расширения для даты: extension Date { } Затем создайте функцию внутри этого расширения: extension Date { func formatDate() -> String { let dateFormatter = DateFormatter() dateFormatter.setLocalizedDateFormatFromTemplate("EEEE, MMM d") return dateFormatter.string(from: self) } } В приведенном выше коде важной частью является то, что вы помещаете в круглые скобки после. setLocalizedDateFormatFromTemplate . В нашем случае EEEE представляет день недели, MMM — первые три буквы месяца, а d — дату. Затем в своем представлении просто вызовите расширение getToday() для даты : let today = Date().formatDate() И вызовите переменную в тексте , чтобы отобразить ее: Text(today) Отформатируйте строку JavaScript в дату SwiftUI Допустим, вы получили строковую дату после получения данных из Firestore или любого другого API в формате гггг-ММ-дд'Т'ЧЧ:мм:сс.SSSZ (например, 2021-02-01T09:45:00.000+02: 00 ). Чтобы преобразовать его в дату SwiftUI, просто передайте строку даты в функцию ниже: func formatStringDate(date: String) -> String { let dateFormatter = DateFormatter() dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ" let newDate = dateFormatter.date(from: date) dateFormatter.setLocalizedDateFormatFromTemplate("MMMM d, yyyy") return dateFormatter.string(from: newDate!) } // Output: February 1, 2021 Пользовательские значения по умолчанию При создании приложения с аутентификацией важно кэшировать данные пользователя. Даже когда пользователь выходит из приложения, данные должны оставаться там до следующего запуска пользователем приложения. Мы не хотим просить пользователя входить в систему каждый раз, когда он открывает приложение, так как это будет плохо для пользователя. UserDefaults — это класс, который делает именно это. Он постоянно кэширует предпочтения пользователя (например, валюту, которую пользователь предпочитает для приложения магазина), а также любые другие данные, которые вам могут понадобиться (идентификатор пользователя, его адрес электронной почты и т. д.). Он работает как пары ключ-значение и очень прост в реализации. Как использовать UserDefaults? В своем представлении добавьте Text . Давайте пока установим его в пустую строку. var body: some View { Text("") } ЗАПИСЬ В USERDEFAULTS Добавьте модификатор .onAppear к тексту, чтобы при появлении текста мы хотели установить адрес электронной почты пользователя. Создайте переменную userEmail в модификаторе и установите для нее все, что хотите. Затем вызовите класс UserDefaults со стандартными настройками ( .standard ) и установите ключ электронной почты в только что созданную переменную userEmail . var body: some View { Text("") .onAppear() { let userEmail = "stephanie@email.com" UserDefaults.standard.set(userEmail, forKey: "email") } } ЧТЕНИЕ ИЗ USERDEFAULTS В верхней части того же файла, сразу после того, как вы определили свою структуру , создайте еще одну переменную, где мы будем читать электронное письмо, которое мы сохранили в наших UserDefaults. let savedEmail = UserDefaults.standard.string(forKey: "email") Наконец, замените пустые строки в Text на переменную saveEmail . Text(savedEmail) Окончательный код Окончательный код будет выглядеть так: struct ContentView: View { let savedEmail = UserDefaults.standard.string(forKey: "email") var body: some View { Text(savedEmail) .padding() .onAppear() { let userEmail = "stephanie@email.com" UserDefaults.standard.set(userEmail, forKey: "email") } } } Другие примеры Вы можете установить любой тип переменной в UserDefaults, она не ограничивается строками. Вот еще несколько примеров: // An integer let userAge = 40 UserDefaults.standard.set(userAge, forKey: "age") // A boolean let userIsLoggedIn = true UserDefaults.standard.set(userIsLoggedIn, forKey: "isLoggedIn") // A date let userLastLoginTime = Date() UserDefaults.standard.set(userLastLoginTime, forKey: "lastLoginTime") Примечание . Не храните слишком много данных в UserDefaults. Храните только важные, так как слишком много данных в UserDefaults может использовать слишком много памяти, а первая загрузка может быть дольше каждый раз, когда пользователь запускает приложение. Ссылка из текста Если вы хотите добавить ссылку на текст в SwiftUI, вам нужно будет вызвать модификатор .onTapGesture , а также UIApplication.shared.open . Одна ссылка Например, если пользователь нажимает на следующий текст, мы хотим, чтобы он перешел на веб-сайт Design+Code. Итак, мы сделаем что-то вроде этого: Text("Visit Design+Code") .onTapGesture { UIApplication.shared.open(URL(string: "https://designcode.io")!, options: [:]) } Несколько ссылок Если у вас есть длинный текст с несколькими ссылками, просто оберните их в HStack : HStack { Text("You agree to our") Text("Terms") .onTapGesture { UIApplication.shared.open(URL(string: "https://designcode.io/terms")!) } Text("and") Text("Privacy policy") .onTapGesture { UIApplication.shared.open(URL(string: "https://designcode.io/privacy")!) } Text(".") } |