🚀 Поделюсь реализации навигации в сторис на SwiftUi
Недавно сделал удобный переход между историями с помощью... прозрачных областей! Решил отказаться от стандартных кнопок и использовать жесты по зонам экрана. Как это работает:
✅ Левая треть экрана — переход к предыдущей истории ✅ Правая треть — следующая история ✅ Центральная часть — пауза/продолжение автоскрола ➕ Прогресс-бар с анимацией и таймером ➕ Закрытие модалки с остановкой таймера
Фишки реализации:
1️⃣Использовал Color.clear с contentShape для невидимых кликабельных зон
2️⃣ Combine для управления таймером и автоматическим переходом
3️⃣ Плавная анимация прогресс-бара через withAnimation
4️⃣ Интеграция с роутером для навигации
5️⃣ DI-контейнер для управления зависимостями
Почему это удобно: Полностью кастомный интерфейс Интуитивные жесты как в популярных соцсетях Контроль над всеми состояниями через ViewModel
👉 Код прикрепил в комментариях. Буду рад услышать ваше мнение и улучшения!
#SwiftUI #iOSDev #MobileDevelopment #ProgrammingTips #CodeShare
P.S. Особенно горжусь системой прогресс-бара — он синхронизирован с таймером и учитывает количество сторис. Попробуйте повторить или предложите свою оптимизацию! 💡
· 19.03
// // StoryDetailView.swift // Navigation SwiftUI // // Created by Maksim Zakharov on 16.02.2025. //
import SwiftUI import Combine
struct StoryDetailView: View { // MARK: - States @ObservedObject private var router: Router @ObservedObject private var viewModel: StoryViewModel
private let story: StoryModel
// MARK: - init init(story: StoryModel) { guard let r = DIContainer.shared.resolve(Router.self), let vm = DIContainer.shared.resolve(StoryViewModel.self) else { fatalError("Dependencies not registered") } self.story = story self.viewModel = vm self.router = r }
var body: some View { ZStack(alignment: .topTrailing) {
StoryView(story: viewModel.currentStory)
ProgressBar( numberOfSections: viewModel.stories.count, progress: viewModel.progress ) .padding(.init(top: 28, leading: 12, bottom: 12, trailing: 12))
// Области для навигации по историям HStack(spacing: 0) { Color.clear .contentShape(Rectangle()) .onTapGesture { viewModel.previousStory() viewModel.restartTimer() } .frame(width: UIScreen.main.bounds.width * 0.3)
Color.clear .contentShape(Rectangle()) .onTapGesture { viewModel.restartTimer() }
Color.clear .contentShape(Rectangle()) .onTapGesture { viewModel.nextStory() viewModel.restartTimer() } .frame(width: UIScreen.main.bounds.width * 0.3) }
CloseButton(action: { viewModel.stopTimer() router.pop() }) .padding(.top, 57) .padding(.trailing, 12) .zIndex(1) } .onAppear { viewModel.startTimer() } .onDisappear { viewModel.stopTimer() } .navigationBarHidden(true) } }
#Preview { StoryDetailView(story: .story1) }
ответить
коммент удалён