iOS: Навигация по-новому

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

С каждый днем все больше разработчиков IOS стремятся свои новые проекты начинать с использованием SwiftUI. И здесь перед ними возникает проблемы в виде реализации устоявшихся представлений о навигации в iOS. Предлагаемые решения от Apple работают весьма часто довольно криво. Это понимают и в самой Apple. По мере развития SwiftUI основной компонент навигации NavigationView был заменен на NavigationStack. И это не просто переименование. Те кто уже использовал NavigationView не готовы от него отказаться, так как его реализация лежала через боль и слезы. Те же кто только входит в мир SUI либо наталкиваются на рекомендации создавать кастомную навигацию, либо смотрят на статьи как разруливать проблемы NavigationView. Новая альтернатива не всем пришлась по-душе, так как на WWDC не продемонстрировали его с лучшей стороны. А она есть. И это хорошая новость! Apple, наконец, освоила паттерн Navigator, которым конкуренты пользовались более 10 лет!

В чем суть: теперь навигация становится возможной даже при помощи передачи пути для навигации. Те кто пользовался DeepLink или UniversalLink возрадуются. Теперь и на их улице будет праздник. Оговорюсь – это не значит что раньше это было невозможно, но теперь не приходится для этого устраивать танцы с бубном.

Чтоб продемонстрировать идею, был набросан минимальный проект, включающий пять экранов, с незамысловатыми названиями: first, second, third и fourh. Эти экраны были объединены следующей схемой переходов:

 

Здесь, сплошной линией со стрелкой обозначен прямой переход на указанный экран. Толстой стрелкой показан переход на четвертый экран по пути навигации. Переходы по нажатию на «Back» и переход на главный экран не обозначены, чтоб не захламлять схему.

Вместо набившим оскомину NavifationLink в приложении был использован обычный Button, так как он значительно лучше поддается кастомизации.

Вся навигация сводится к передаче массива с условными названиями экранов в переменную пути. NavigarionStack пройдет все цепочку навигации автоматически, и покажет последний экран в цепочке.

Так для того, чтоб показать экран «Fourth» с сохранением всей цепи навигации достаточно в переменную передать массив [«first», «second», «third», «fourh»]. Соответственно, и возврат по «Back» будет происходить в обратном порядке.

Button { model.path = ["first", "second", "third", "fourth"] }
                         label: { ButtonContent("The furthest view") }

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

В отличите от UIKit, NavigationStack не хранит в себе состояния предыдущих экранов. Таком образом, при возврате View и его ViewModel будет воссоздана с нуля – это следует учитывать при создании архитектуры UI когда необходимо сохранить пользовательские данные, или вернуть состояние Scroll / таблицы к предыдущей ячейке.

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

class Coordinator: ObservableObject {
    @Published var path: [String] = []
    
    func resolve(pathItem:String) -> some View {
        Group {
            switch pathItem {
            case "first": FirstView()
            case "second": SecondView()
            case "third": ThirdView()
            case "fourth": FourthView()
            default:
                EmptyView()
            }
        }
    }
}

Для возврата на корневой экран, достаточно массиву пути присвоить пустое значение:

Button { model.path = [] } label: { ButtonContent("Root View") }

Скорее всего, NavigationStack все еще хранит в себе множество неприятных сюрпризов, доставшихся по наследству от NavigationView. Первым бросается в глаза то, что при переходе на более чем один уровень вложенности – слетает анимация. Это происходит если путь заменяется единственным элементом в массиве. При добавлении нового элемента в массив  – такого не происходит.

Код доступен на GitHub

Обсудить можно на телеграмм канале.

Предыдущая статья о кастомной навигации доступна на хабре.

Источник: https://habr.com/ru/post/694946/


Интересные статьи

Интересные статьи

Привет, это Дима, бэкенд-разработчик из Hawking Bros. Сегодня я расскажу о решении задачи, с которой мы столкнулись при реализации нестандартной логики инфоблоков на проекте с битриксовой многосайтово...
Статья направлена на решение проблемы "зависания" выгрузки результатов опросов при использовании модуля Vote 1С-Битрикс в случае, если в опросе много вопросов или ответов. Так-же в статье реализована ...
В данной статье мы рассмотрим систему аутентификации пользователей и внешних систем в личном кабинете через сервер аутентификации Blitz Identity Provider. Согласно требованиям проекта, который мы...
Предыстория Когда-то у меня возникла необходимость проверять наличие неотправленных сообщений в «1С-Битрикс: Управление сайтом» (далее Битрикс) и получать уведомления об этом. Пробле...
Есть статьи о недостатках Битрикса, которые написаны программистами. Недостатки, описанные в них рядовому пользователю безразличны, ведь он не собирается ничего программировать.