Irbisa · Шпаргалка
Основы SwiftUI
Протокол View, модификаторы, контейнеры, навигация, списки
Что такое SwiftUI и в чём его плюсы и минусы по сравнению с UIKit?
Что значит «декларативный UI» и почему view в SwiftUI сделаны структурами, а не классами?
Что такое протокол
View, что такоеbodyи что означаетsome View?struct ProfileHeader: View { let name: String let isPro: Bool // The real type is VStack<TupleView<(Text, Text?)>>; `some View` hides it var body: some View {…Что делают
@main,AppиSceneв SwiftUI-приложении?@mainstruct NotesApp: App { @State private var store = NotesStore() // @Observable model owned by the app @UIApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate @Environment(\.scenePhase) private var scenePhase…Как
VStack,HStackиZStackраскладывают дочерние view и когда вместо них нуженLazyVStack?VStack(alignment: .leading, spacing: 4) { Text("Title").font(.headline) Text("Subtitle").foregroundStyle(.secondary)} HStack {…Как выглядит каждый из трёх бейджей и почему перестановка двух модификаторов меняет результат?
// Badge AText("Sale") .padding() .background(.red) // Badge BText("Sale") .background(.red) .padding() // Badge CText("Sale") .background(.red) .frame(width: 200)Как сделать переиспользуемый стиль в SwiftUI: свой
ViewModifierилиButtonStyle?struct CardStyle: ViewModifier { func body(content: Content) -> some View { content .padding() .background(.regularMaterial, in: .rect(cornerRadius: 16)) }…Вы растянули
Textчерез.frame(maxWidth: .infinity), а он остался по центру. Чем различаются выравнивания в SwiftUI?// wrong: the frame stretches, the text stays in its centreText("Title").frame(maxWidth: .infinity) // right: tell the frame where to put its childText("Title").frame(maxWidth: .infinity, alignment: .leading)…Картинка
Imageиз бандла игнорирует.frame, а.scaledToFill()вылезает на соседей: в каком порядке ставить модификаторы и чего не делает за васAsyncImage?// wrong: a bundled image ignores the proposed size until it is resizableImage("cover").frame(width: 120, height: 120) // full size, overflowing // right: resizable, content mode, frame, then clip what .fill spillsImage("cover") .resizable()…Как собрать
ListсForEachи зачем каждой строке стабильныйid?struct Todo: Identifiable { let id: UUID // stable: stored on the model, not generated in body var title: String var isDone = false}…Как собрать форму в SwiftUI с
TextField,SecureField,ToggleиPicker?struct SignUpForm: View { @State private var email = "" @State private var password = "" @State private var plan: Plan = .free @State private var wantsNews = false…Как показать sheet или alert в SwiftUI и когда использовать форму
item:вместоisPresented:?struct InboxView: View { @State private var editing: Message? // nil means no sheet @State private var pendingDelete: Message? var body: some View { List(messages) { message in…Когда использовать
LazyVGrid, а когдаGrid, и как сделать, чтобы число колонок зависело от ширины экрана?// Gallery: as many 120-pt columns as fit, cells built lazilyScrollView { LazyVGrid(columns: [GridItem(.adaptive(minimum: 120), spacing: 8)], spacing: 8) { ForEach(photos) { photo in Thumbnail(photo).frame(height: 120) }…Как писать превью SwiftUI для view с несколькими состояниями или с зависимостью от сервиса?
#Preview("Default") { ProfileCard(user: .preview)} #Preview("Long bio, largest text") { ProfileCard(user: User(id: UUID(), name: "Alice",…Когда использовать
List, а когдаScrollViewсLazyVStack?Как SwiftUI определяет размер view: за кем последнее слово, за родителем или за ребёнком?
// .frame proposes a size and positions whatever the child returnsText(longString).frame(width: 100) // Text wraps into 100 pt // fixedSize asks for the ideal size: stop the vertical truncationText(paragraph) .fixedSize(horizontal: false, vertical: true)…Как работает
NavigationStackи как переходить между экранами из кода или по диплинку?enum Route: Hashable { case profile(userID: String) case settings} struct RootView: View {…Тап по строке этого списка ничего не делает, а Xcode пишет предупреждение о навигации: в чём ошибка?
struct ShopView: View { let products: [Product] var body: some View { NavigationStack { List(products) { product in NavigationLink(product.name, value: product) .navigationDestination(for: Product.self) { product in ProductDetail(product: product) } } .navigationTitle("Shop") } }}Что выведет этот код при появлении экрана и при каждом тапе и что это говорит об
init,bodyиonAppear?struct Child: View { init() { print("init") } var body: some View { let _ = print("body") Text("Child").onAppear { print("appear") } }} struct Parent: View { @State private var count = 0 var body: some View { VStack { Text("Taps: \(count)"); Child(); Button("Tap") { count += 1 } } }}Найдите баг: при каждом возвращении на этот экран секундомер считает всё быстрее и продолжает тикать, пока поверх открыт другой экран.
struct StopwatchView: View { @State private var seconds = 0 var body: some View { Text("\(seconds) s") .font(.largeTitle.monospacedDigit()) .onAppear { Task { do { while true { try await Task.sleep(for: .seconds(1)) seconds += 1 } } catch {} } } }}Что
@ViewBuilderделает под капотом и почему возвращатьAnyViewсчитается плохой привычкой?// Your own container that takes a builderstruct Card<Content: View>: View { @ViewBuilder var content: Content var body: some View { VStack(alignment: .leading, spacing: 8) { content }…Чем
withAnimationотличается от модификатора.animationи почему одноаргументный.animation(_:)объявили устаревшим?struct Panel: View { @State private var isExpanded = false @State private var count = 0 var body: some View { VStack {…В строке списка есть кнопка «звезда» и кнопка удаления, и тап по любой запускает обе плюс навигацию строки: что происходит?
// wrong: .automatic buttons inherit the row's tap regionList(messages) { message in NavigationLink(value: message) { HStack { Text(message.title) Spacer()…После сохранения на четвёртом экране стека нужно вернуться в корень, а тот же флоу работает и внутри sheet: как это построить?
@Observable @MainActorfinal class Router { var path: [Route] = [] } struct RootView: View { @State private var router = Router() @State private var sheetRouter = Router()…Найдите баг: при выборе другой статьи заголовок обновляется, а этот web view продолжает показывать первую страницу.
struct WebView: UIViewRepresentable { let url: URL func makeUIView(context: Context) -> WKWebView { let webView = WKWebView() webView.load(URLRequest(url: url)) return webView } func updateUIView(_ webView: WKWebView, context: Context) {}} // In the parent: WebView(url: selectedArticle.url)