Connect with us

Разработка

Производительный SwiftUI: используем UIKit

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

Опубликовано

/

     
     

Разработка под платформу Apple претерпела значительные изменения. Раньше мы считали, что создание ядра приложения на основе UIKit и использование SwiftUI для некоторых экранов — это хорошая идея. Сейчас мы рассмотрим разработку основной части приложения с использованием SwiftUI, а также использование UIKit в сценариях, где производительность действительно имеет значение.

Когда я говорю «основная часть приложения», я имею в виду жизненный цикл приложения и навигацию. Используя UIKit, мы можем построить навигацию, используя довольно популярный шаблон Координатор, и пушить представления SwiftUI, обернув их в UIHostingController. Мы по-прежнему можем использовать делегат приложения для управления жизненным циклом приложения. Это отлично работает с точки зрения производительности и унаследованной кодовой базы. Но, к сожалению, у такого подхода есть свои минусы.

struct Coordinator {
    let navigation: UINavigationController
    
    init(navigation: UINavigationController) {
        self.navigation = navigation
    }
    
    func start() {
        let auth = UIHostingController(rootView: AuthView())
        navigation.viewControllers = [auth]
    }
    
    func startMainFlow() {
        let main = UIHostingController(rootView: MainView())
        navigation.viewControllers = [main]
    }
}


class SceneDelegate: UIResponder, UIWindowSceneDelegate {
    private lazy var navigation = UINavigationController()
    private lazy var coordinator = Coordinator(navigation: navigation)
    
    var window: UIWindow?

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        window = UIWindow(frame: UIScreen.main.bounds)
        window?.rootViewController = navigation
        window?.makeKeyAndVisible()
        coordinator.start()
    }
}

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

Навигация в SwiftUI была очень слабой, очень трудно было построить глубокую перелинковку и модульный подход. Но с NavigationStack, выпущенным в iOS 16, мы получили довольно новый API, основанный на данных, который позволяет нам легко создавать функциональность глубоких ссылок.

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

@main
struct ExamplesApp: App {
    @State private var path = NavigationPath()
    @State private var store = Store()
    
    var body: some Scene {
        WindowGroup {
            NavigationStack(path: $path) {
                HugeListView(items: store.items)
                    .navigationDestination(for: Item.self) { item in
                        // push item details view
                    }
                    .onOpenURL { url in
                        // parse url and push next view
                    }
            }
        }
    }
}

Можно ожидать, что жизненный цикл приложений SwiftUI и недавно появившийся навигационный API позволят нам реализовать практически все необходимые функции: глубокие ссылки, восстановление состояния и т.д. К сожалению, в некоторых случаях производительность SwiftUI может давать сбои. Особенно если у вас есть бесконечные коллекции данных, такие как социальные ленты или макеты для календарей.

Не волнуйтесь, потому что у нас уже есть решение на этот случай под названием UIHostingConfiguration. Это новый тип конфигурации UITableViewCell или UICollectionViewCell, позволяющий нам встраивать представления SwiftUI в ячейку, но при этом использовать производительные возможности UICollectionView по работе с очередями. Этот подход отлично работает там, где производительность действительно имеет значение.

struct HugeListView: UIViewRepresentable {
    let items: [Item]
    
    func makeUIView(context: Context) -> UITableView {
        let tableVIew = UITableView()
        tableVIew.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
        tableVIew.dataSource = context.coordinator
        return tableVIew
    }
    
    func updateUIView(_ uiView: UITableView, context: Context) {
        uiView.reloadData()
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator(items: items)
    }
    
    final class Coordinator: NSObject, UITableViewDataSource {
        let items: [Item]
        
        init(items: [Item]) {
            self.items = items
        }
        
        func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            return items.count
        }
        
        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let item = items[indexPath.row]
            let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
            cell.contentConfiguration = UIHostingConfiguration {
                ItemView(item: item)
            }
            
            return cell
        }
    }
}

Помните, что UIHostingConfiguration создается только для встраивания представлений SwiftUI в UICollectionView или UITableView. Не используйте NavigationLink внутри UIHostingConfiguration. Вместо этого используйте координатор для обработки выбора и передавайте значения в экземпляр NavigationStack.

SwiftUI значительно развился, что делает его подходящим выбором для создания ядра современных приложений для iOS, особенно с улучшенной системой навигации в iOS 16. Однако UIKit остается незаменимым для обработки чувствительных к производительности сценариев, особенно при работе с большими наборами данных или сложными UI структурами. Надеюсь, этот пост был вам полезен.

Источник

Если вы нашли опечатку - выделите ее и нажмите Ctrl + Enter! Для связи с нами вы можете использовать info@apptractor.ru.
Telegram

Популярное

Сообщить об опечатке

Текст, который будет отправлен нашим редакторам: