Skip to content

Latest commit

 

History

History
152 lines (109 loc) · 11.7 KB

Lecture11.md

File metadata and controls

152 lines (109 loc) · 11.7 KB

SwiftUI and UIKit

В тази лекция, ще се научим как да интегрираме SwiftUI в UIKit проект, който няма SwiftUI компоненти. Ще можем да изградим здрава връзка между съществуващите модели и контроли в един класически проект. След това ще се научим как да опаковаме UIView или UIViewController, така че да може да ги ползваме в SwiftUI проект. Това ще ни даде възможност да изпозлваме и Canvas изгледа, за да можем да "строим" интерфейсите на своите приложения по-лесно. За завършек, ще приключим с основните Gesture детектори - абстракции, които "разпознават" различни видове жестове и активират опрделени функции в приложението, при настъпване на дадените жестове.

Интегриране на SwiftUI компонент

Голяма част от приложенията написани за iOS, са базирани на класическата архитектура, която е стандарт за UIKit проекти. В тези проекти, няма SwiftUI. Te разчитата на Storyboard, за да дефинират потребителския интерфейс или на различни .xib (конвертира се до .nib) файлове.

Storyboard-ът е специален формат XML файл, който можем да редактираме с интерфейс билдъра - специален режим на (вече част от) Xcode.

Без да влизаме в допълнителни детайли, трябва да споменем, че SwiftUI е достъпен в всеки UIKit проект. iOS предоставя специален вю-контролер, който позволява да интегрирате произволно SwiftUI вю. В следния пример, виждаме как се използва UIHostingController, за да можем да покажем предварително имплементираното вю.

// хендлър, който ще бъде активиран при натискане на бутон
@IBAction func buttonPressedHandler(_ sender: Any) {
    // конкретна имплементация
    let swiftUIViewController = UIHostingController(rootView: InfoScreenView())
    self.navigationController?.pushViewController(swiftUIViewController, animated: true)
}

Ето и конкретната имплементация на примерното InfoScreenView:

import SwiftUI

struct InfoScreenView: View {
    var body: some View {
        Text("Some SwiftUI goes here")
    }
}

След като имате конкретна инстанция на UIHostingController може да го покажете по различни начини и в правилния момент.

Може би вече се чудите, какви механизми можем да използваме, за да боравим с данни в SwiftUI. Отгворът е - всичко, което сме научили с малки изключения (@Binding не може да се ползва директно).

@State и @StateObject - за да дефинираме състояние вътре в самото вю, което може да се предава надолу по йерархията от SwiftUI вю-та.

@EnvironmentObject и @ObservedObject позволяват да свържете вече съществуващия си модел (разбира се като имплементирате протокола ObservableObject и като маркирате дадени полета за @Published).

Полетата маркирани като @Published ще обновяват изгледа на SwiftUI компонентите, ако те зависят от тях.

Ето един минимален пример, който илюстрира как можем да свържем модел със SwiftUI вю.

моделът
вю-то
Ето така го подаваме

Ето така го подаваме към компонента и той вече е достъпен в новото вю.

Вариантът, който показахме горе не е единствения начин да интегрираме SwiftUI компоненти в UIKit проект. Ако ползваме таблица или колекция има начин да дефинираме конкретна клетка посредством SwiftUI (но това е по-скоро частен случай и е достъпен с iOS 16). Това нововъведение позволява по-лесно и интуитивно да се описва всяка клетка и нейния изглед с новите средства. Изграждането на интерфейса на приложението е значително по-лесно с този нов механизъм.

Без да навлизаме в излишни детайли, ще покажем примерен код, а любознателния читател, който има желание да изпозлва тази функционалност ще трябва да набави необходимите знания от документацията тук.

Обработката на данни и обновяването на клетките не е толкова лесно, но можем да изпозлваме вече добре познатия ни механизъм с ObservableObject.

func tableView(_ tableView: UITableView,
                   cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    let cell = tableView.dequeueReusableCell(
        withIdentifier: cellReuseID,
        for: indexPath
    )
    // приемаме, че items е колекцията от елементите,
    // които показваме таблицата
    let item = items[indexPath.row]

    cell.contentConfiguration = UIHostingConfiguration {
        HStack {
            Text(item.title)
            Spacer()
            Image(systemName: "star.fill")
        }
    }
    return cell
}

Трябва да отбележем, че няма вариант, в който да интегрираме конкретна инстанция на определено SwiftUI вю, понеже то трябва да "живее" някъде. Това някъде е конкретна инстанция на UIHostingController, който ще се грижи за всичките нужди, които има SwiftUI.

Как да интегрираме UIView в SwiftUI?

Как да интегрираме UIViewController в SwiftUI?

Gestures - жестове

Преди да стигнем до жестовете трябва да разберем как работи механизмът за засичане на допър върху екрана на устройството.

Съвременните устройства разполагат с капацитивни екрани. Т.е. те са изградени от разлини слове материали, които могат да улавят провеждането на нисък ток от нашите пръсти на ръцете или съответните други аксесоари, които позлваме. Не е нужно да се прилага сила, за да се засича местоположението на "пръстите" контактуващи с екрана.

От там хардуера предава на операционната система тези точки като координати, а те от своя страна стигат до нашето приложение. iOS се грижи да предаде координатите до конкретното вю.

Ако дадено вю улавя "тъчовете", то вю-тата под него няма да получават никакви тъчове. (Това поведени може да бъде променено при желание от страна на програмистта, ако е необходимо.)

Ето и един пример, който премахва възможността текст-а да хваща "тъчовете". Те минават директно към вю-то под него.

Text("Hello.").allowsHitTesting(false)

Добре вече ние ясно, че със своите действия (комуникация) с устройстовто, имаме множество от тъчове. Т.е. можем да си представим един поток от специални ивенти, които всъщност са координати по екрана и показват кой пръст точно ги е създал.

iPhone може да следи до пет пръста още от самото начало. A `iPadѕ до десет във всеки един момент.

Тогава много лесно можем да обясним какво са жестовите детектори - това са коклретни структури, които позовляват разпознаването на конкретни последователности в този поток от "тъч" (touch) данни. При разпознаване на тази последователност имаме възможност да активираме някаква функция. Това е най-общия вид на тези обекти.

iOS идва с няколко различни жестови детектори. Но нека да разгледаме най-основния.

ТаpGesture - той ни позовлява да засечем, кога потребителя тап-ва по опделен елемент.

Ето и конкретен пример:

struct ContentView: View {
    @State private var scale = 1.0
    
    var body: some View {
        VStack {
            Text("Gestures!")
                .font(.title)
            Image(systemName: "iphone")
                .imageScale(.large)
                .foregroundColor(.accentColor)
                .background(Color.gray)
                .scaleEffect(scale)
                .gesture(
                    TapGesture()
                        .onEnded { _ in
                            scale += 0.1
                        }
                )
        }
        .padding()
    }
}

Всички детектори на жестове (жест-детектори) добавяме чрез модификатора gesture(). Можем да използваме конструктора да зададем конкретна конфигурация на съответния жест-детектор. А с onEnded модификаторът, подаваме клоужър, който се изпълнява при засичане на съответния жест.

LongPressGesture

DragGesture

@GestureState

Повече от един жест детекор чрез simultaneously()