LazyVStack vs VStack vs List в SwiftUI: https://proglib.io/w/c6130ba9
YouTube
Performance between LazyVStack, VStack and List #SwiftUI
Hello guys!
I started this channel talking about stacks in SwiftUI, today we will talk about another stack: LazyVStack.
Let's see what's the difference between regular VStack and List. By the way, we will focus only on vertical stacks, but this also will…
I started this channel talking about stacks in SwiftUI, today we will talk about another stack: LazyVStack.
Let's see what's the difference between regular VStack and List. By the way, we will focus only on vertical stacks, but this also will…
Видеоруковдство по использованию NavLink в SwiftUI:
✅ Creating basic links
✅ Lists and links
✅ Programmatic navigation
✅ Unwind segues
✅ Tags and selection
https://proglib.io/w/20075c43
✅ Creating basic links
✅ Lists and links
✅ Programmatic navigation
✅ Unwind segues
✅ Tags and selection
https://proglib.io/w/20075c43
YouTube
Understanding NavLink in SwiftUI
In this video, Mohammad Azam will demonstrate how to use the NavLink in SwiftUI. Azam will cover creating basic navigation links, navigation links inside a list, conditional links and even unwind segues.
#SwiftUI, #iosdevelopment
Gist: https://gist.g…
#SwiftUI, #iosdevelopment
Gist: https://gist.g…
👍1
Практический пример:
Поиск является обычной функцией во многих приложениях. Когда пользователь вводит поисковый запрос, мы часто хотим избежать отправки нового запроса на сервер при каждом изменении запроса. Вместо этого мы могли бы отложить запрос и отправить его только после того, как пользователь перестанет печатать на определенное время.
Реализация поиска в SwiftUI
struct SearchView: View {
@State private var userSearchInput: String = «»
var body: some View {
VStack {
// Show loading indicator, error, or results
}
.searchable(text: $userSearchInput)
.onChange(of: userSearchInput) { _, newValue in
viewModel.updateSearch(userSearchInput: newValue)
}
}
}
1. Мы используем модификатор поиска SwiftUI для настройки отображения поля поиска.
2. Мы используем переменную @State для передачи в качестве привязки к модификатору, доступному для поиска. Она будет обновляться автоматически по мере изменения пользователем поискового запроса.
3. Мы подписываемся на изменения в поиске, чтобы уведомить нашу модель просмотра о том, что ей может потребоваться обновить результаты.
Реализация в ViewModel
Далее посмотрим, как может быть реализована ViewModel:
class SearchViewModel {
private var userSearchInputSubject = PassthroughSubject()
private var cancellables = Set()
init() {
userSearchInputSubject
.debounce(for: 0.5, scheduler: RunLoop.main)
.sink { [weak self] searchInput in
self?.reload(with: searchInput)
}
.store(in: &cancellables)
}
func updateSearch(userSearchInput: String?) {
userSearchInputSubject.send(userSearchInput)
}
}
Используя оператор debounce, мы гарантируем, что запросы на сервер будут отправляться только тогда, когда пользователь перестанет печатать на полсекунды, что сокращает количество ненужных запросов и повышает общую производительность нашего приложения.
#гайд #SwiftUI
Please open Telegram to view this post
VIEW IN TELEGRAM
❤1👍1
В iOS 18 в SwiftUI появились liquid glass-стили — полупрозрачные, «стеклянные» кнопки и элементы.
Обычная кнопка:
Button("Add", action: addItem)
— рисуется в стандартном системном стиле.
Если применить
.glass
:Button("Add", action: addItem)
.buttonStyle(.glass)
кнопка получает эффект стекла, глубину и анимацию нажатия. Цвет можно задать через .tint, но система полностью контролирует размеры и отступы.
Есть вариант
.glassProminent
для основных действий, но пока он не работает в Xcode 26.0 beta.Отдельно существует модификатор
.glassEffect()
, который можно применить к любому виду:Text("Hello")
.padding()
.glassEffect()
Это просто придаёт представлению полупрозрачный фон, без анимаций и встроенного стиля — всё остальное нужно оформить вручную.
В
Toolbar
многие кнопки автоматически получают стеклянный стиль, особенно в слотах подтверждения или отмены, и отключить это пока нельзя.Стоит отметить, что стеклянные кнопки не заменяют пользовательские
ButtonStyles
. Если нам нужны фирменные кнопки для конкретного приложения или динамические кнопки, внешний вид которых зависит от состояния, то пользовательские стили по-прежнему будут подходящим инструментом. Но если мы хотим использовать системный внешний вид и соответствовать развивающейся эстетике Apple, то стеклянные стили — это быстрый способ добиться желаемого.
А вы уже использовали .glass или .glassEffect()? Делитесь впечатлениями в комментариях
#MiddlePath #SwiftUI #iOS
Please open Telegram to view this post
VIEW IN TELEGRAM
❤3👍1
NavigationPath
NavigationStack
и NavigationPath
в SwiftUI предоставляют мощный и гибкий способ выполнять программную навигацию в приложении. Когда вы управляете навигацией, часто возникает необходимость программно открывать (push) и закрывать (pop) экраны. NavigationPath
позволяет делать это, сохраняя типобезопасность и гибкость.🔹
NavigationStack(root:)
Инициализатор по умолчанию задаёт корень навигационной иерархии и управляет путём навигации “за кулисами”. Если вы хотите получить больший контроль и управлять навигацией программно, можно хранить путь в переменной
@State
и передавать его в инициализатор NavigationStack(path:root:)
.Параметр path должен быть
Binding<Data>
, и есть два способа его использования.Первый способ — использовать массив определённого типа, который реализует протокол
Hashable
. Это удобно, если весь стек навигации основан на одном типе данных.@State private var path: [Color] = []
NavigationStack(path: $path) {
List {
ForEach(colors, id: \.self) { color in
Button {
path.append(color)
} label: {
...
}
}
}
.navigationDestination(for: Color.self) { color in
VStack {
color
...
Button("Pop to root") {
path.removeAll()
}
}
...
}
}
В примере выше навигационный стек поддерживается массивом объектов
Color
, который выступает в роли NavigationPath
. Каждый раз, когда элемент добавляется в path, модификатор navigationDestination(for:)
показывает соответствующий экран. Вызов path.removeAll()
очищает стек и возвращает пользователя к корневому экрану.Этот подход идеально подходит для чистой, типобезопасной навигации с минимальной настройкой, особенно если вы работаете с одним типом данных.
Когда вы находитесь в корневом экране, массив пуст.
При переходе вперёд — он заполняется элементами, где последний элемент массива соответствует текущему экрану.
Чтобы открыть новый экран — добавляем элемент, чтобы вернуться назад — удаляем последний.
NavigationPath
для нескольких типовЕсли навигационный стек может содержать разные типы данных (например,
Color
, String
или пользовательские типы), лучше использовать NavigationPath
. Он работает как type-erased список данных, но при этом сохраняет достаточно информации, чтобы SwiftUI знал, какой экран показать для каждого типа.@State private var path = NavigationPath()
NavigationStack(path: $path) {
List {
Section("Colors") {
ForEach(colors, id: \.self) { color in
Button {
path.append(color)
} label: {
...
}
}
}
Section("Genres") {
ForEach(genres, id: \.self) { genre in
Button {
path.append(genre)
} label: {
...
}
}
}
}
.navigationDestination(for: Color.self) { color in
VStack {
...
Button("Pop to root") {
path.removeLast(path.count)
}
}
...
}
.navigationDestination(for: String.self) { genre in
VStack {
...
Button("Pop to root") {
path.removeLast(path.count)
}
}
...
}
}
С
NavigationPath
вы можете добавлять разные типы данных в стек. Для каждого типа нужно задать отдельный navigationDestination(for:destination:)
, чтобы описать, как отображать соответствующий экран.Если вы добавите значение в
NavigationPath
, но не определите navigationDestination
для его типа,ошибки компиляции не будет — однако пользователь увидит пустой экран с предупреждением.
Такой подход более гибкий, особенно для приложений, навигация в которых зависит от различных моделей данных.
#PixelPerfect #MiddlePath #SwiftUI
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Новый язык дизайна Apple представил эффект светящейся анимированной обводки, которая изящно и динамично подсвечивает формы и компоненты. Давайте рассмотрим, как воссоздать этот эффект в SwiftUI с помощью многоразовых расширений.
🔹 Расширения для View
extension View {
@MainActor
func intelligenceBackground<S: InsettableShape>(in shape: S) -> some View {
background(shape.intelligenceStroke())
}
@MainActor
func intelligenceOverlay<S: InsettableShape>(in shape: S) -> some View {
overlay(shape.intelligenceStroke())
}
}
🔹 Базовая реализация для фигур
extension InsettableShape {
@MainActor
func intelligenceStroke(
lineWidths: [CGFloat] = [6, 9, 11, 15],
blurs: [CGFloat] = [0, 4, 12, 15],
updateInterval: TimeInterval = 0.4
) -> some View {
IntelligenceStrokeView(
shape: self,
lineWidths: lineWidths,
blurs: blurs,
updateInterval: updateInterval
)
.allowsHitTesting(false)
}
}
🔹 Рендеринг слоёв свечения
private struct IntelligenceStrokeView<S: InsettableShape>: View {
let shape: S
let lineWidths: [CGFloat]
let blurs: [CGFloat]
let updateInterval: TimeInterval
@Environment(\.accessibilityReduceMotion) private var reduceMotion
@State private var stops: [Gradient.Stop] = .intelligenceStyle
var body: some View {
let layerCount = min(lineWidths.count, blurs.count)
let gradient = AngularGradient(stops: stops, center: .center)
ZStack {
ForEach(0..<layerCount, id: \.self) { i in
shape
.strokeBorder(gradient, lineWidth: lineWidths[i])
.blur(radius: blurs[i])
.animation(
reduceMotion ? nil : .easeInOut(duration: 0.5 + Double(i) * 0.2),
value: stops
)
}
}
.task {
while !Task.isCancelled {
stops = .intelligenceStyle
try? await Task.sleep(for: .seconds(updateInterval))
}
}
}
}
🔹 Цветовая палитра
private extension Array where Element == Gradient.Stop {
static var intelligenceStyle: [Gradient.Stop] {
let colors = [
Color(red: 188/255, green: 130/255, blue: 243/255),
Color(red: 245/255, green: 185/255, blue: 234/255),
Color(red: 141/255, green: 159/255, blue: 255/255),
Color(red: 255/255, green: 103/255, blue: 120/255),
Color(red: 255/255, green: 186/255, blue: 113/255)
]
return colors
.map { Gradient.Stop(color: $0, location: Double.random(in: 0...1)) }
.sorted { $0.location < $1.location }
}
}
🔹 Использование
// Фон
Text("Текст")
.padding(22)
.intelligenceBackground(in: .capsule)
// Наложение
Text("Текст")
.padding(22)
.intelligenceOverlay(in: .rect(cornerRadius: 22))
🔹 Заключение
Эта реализация показывает, как объединить несколько обводок, размытий и анимированных градиентов для достижения эффекта свечения, аналогичного интерфейсу Apple Intelligence. Результат работает с любым объектом
InsettableShape
. Его можно использовать для современной и выразительной подсветки кнопок, карточек или текстовых контейнеров.#PixelPerfect #MiddlePath #SwiftUI
Please open Telegram to view this post
VIEW IN TELEGRAM
❤3