programing

SwiftUI NavigationLink는 클릭하지 않고 목적지 보기를 즉시 로드합니다.

minimums 2023. 8. 21. 21:00
반응형

SwiftUI NavigationLink는 클릭하지 않고 목적지 보기를 즉시 로드합니다.

다음 코드 사용:

struct HomeView: View {
    var body: some View {
        NavigationView {
            List(dataTypes) { dataType in
                NavigationLink(destination: AnotherView()) {
                    HomeViewRow(dataType: dataType)
                }
            }
        }
    }
}

이상한 것은, 언제.HomeView표시됩니다.NavigationLink즉시 로드AnotherView결과적으로, 모두AnotherView아직 화면에 표시되지 않더라도 종속성도 로드됩니다.사용자가 행을 클릭하여 표시해야 합니다. 내AnotherView포함DataSource여러 가지 일이 일어나는 곳에서문제는 그 전체입니다.DataSource일부 타이머 등을 포함하여 이 시점에서 로드됩니다.

내가 뭔가 잘못하고 있는 것일까요?그런 식으로 다루는 방법, 그것은.AnotherView사용자가 이 버튼을 누르면 로드됩니다.HomeViewRow?

이 문제를 해결하기 위한 가장 좋은 방법은 레이지 뷰를 사용하는 것입니다.

struct NavigationLazyView<Content: View>: View {
    let build: () -> Content
    init(_ build: @autoclosure @escaping () -> Content) {
        self.build = build
    }
    var body: Content {
        build()
    }
}

그러면 탐색 링크는 다음과 같습니다.표시할 보기를 내부에 배치합니다.()

NavigationLink(destination: NavigationLazyView(DetailView(data: DataModel))) { Text("Item") }

편집: @MwcsMac의 답변을 참조하여 View 생성을 폐쇄 내부로 감싸고 뷰가 렌더링된 후에만 초기화하는 보다 깨끗한 솔루션을 확인할 수 있습니다.

그것은 관습이 필요합니다.ForEach함수 작성기가 식을 평가해야 하므로 요청하는 작업을 수행합니다.

NavigationLink(destination: AnotherView()) {
    HomeViewRow(dataType: dataType)
}

표시할 수 있는 각 행에 대해HomeViewRow(dataType:)어떤 경우에AnotherView()또한 초기화해야 합니다.

그래서 이것을 피하기 위한 관습.ForEach필요합니다.

import SwiftUI

struct LoadLaterView: View {
    var body: some View {
        HomeView()
    }
}

struct DataType: Identifiable {
    let id = UUID()
    var i: Int
}

struct ForEachLazyNavigationLink<Data: RandomAccessCollection, Content: View, Destination: View>: View where Data.Element: Identifiable {
    var data: Data
    var destination: (Data.Element) -> (Destination)
    var content: (Data.Element) -> (Content)
    
    @State var selected: Data.Element? = nil
    @State var active: Bool = false
    
    var body: some View {
        VStack{
            NavigationLink(destination: {
                VStack{
                    if self.selected != nil {
                        self.destination(self.selected!)
                    } else {
                        EmptyView()
                    }
                }
            }(), isActive: $active){
                Text("Hidden navigation link")
                    .background(Color.orange)
                    .hidden()
            }
            List{
                ForEach(data) { (element: Data.Element) in
                    Button(action: {
                        self.selected = element
                        self.active = true
                    }) { self.content(element) }
                }
            }
        }
    }
}

struct HomeView: View {
    @State var dataTypes: [DataType] = {
        return (0...99).map{
            return DataType(i: $0)
        }
    }()
    
    var body: some View {
        NavigationView{
            ForEachLazyNavigationLink(data: dataTypes, destination: {
                return AnotherView(i: $0.i)
            }, content: {
                return HomeViewRow(dataType: $0)
            })
        }
    }
}

struct HomeViewRow: View {
    var dataType: DataType
    
    var body: some View {
        Text("Home View \(dataType.i)")
    }
}

struct AnotherView: View {
    init(i: Int) {
        print("Init AnotherView \(i.description)")
        self.i = i
    }
    
    var i: Int
    var body: some View {
        print("Loading AnotherView \(i.description)")
        return Text("hello \(i.description)").onAppear {
            print("onAppear AnotherView \(self.i.description)")
        }
    }
}

50개 항목의 목록을 가지고 있었을 수도 있는 동일한 문제가 있었습니다. 그러면 API를 호출하는 세부 보기를 위해 50개의 보기를 로드했습니다(그 결과 50개의 추가 이미지가 다운로드되었습니다).

제 대답은 화면에 보기가 나타날 때 실행해야 하는 모든 논리를 트리거하는 데 사용하는 것이었습니다(예: 타이머 설정).

struct AnotherView: View {
    var body: some View {
        VStack{
            Text("Hello World!")
        }.onAppear {
            print("I only printed when the view appeared")
            // trigger whatever you need to here instead of on init
        }
    }
}

iOS 14 Swift의 경우UI.

게시물을 기반으로 뷰 수식어를 사용하여 느린 탐색 목적지 로드를 위한 비우호적인 솔루션입니다.

extension View {
    func navigate<Value, Destination: View>(
        item: Binding<Value?>,
        @ViewBuilder content: @escaping (Value) -> Destination
    ) -> some View {
        return self.modifier(Navigator(item: item, content: content))
    }
}

private struct Navigator<Value, Destination: View>: ViewModifier {
    let item: Binding<Value?>
    let content: (Value) -> Destination
    
    public func body(content: Content) -> some View {
        content
            .background(
                NavigationLink(
                    destination: { () -> AnyView in
                        if let value = self.item.wrappedValue {
                            return AnyView(self.content(value))
                        } else {
                            return AnyView(EmptyView())
                        }
                    }(),
                    isActive: Binding<Bool>(
                        get: { self.item.wrappedValue != nil },
                        set: { newValue in
                            if newValue == false {
                                self.item.wrappedValue = nil
                            }
                        }
                    ),
                    label: EmptyView.init
                )
            )
    }
}

다음과 같이 부릅니다.

struct ExampleView: View {
    @State
    private var date: Date? = nil
    
    var body: some View {
        VStack {
            Text("Source view")
            Button("Send", action: {
                self.date = Date()
            })
        }
        .navigate(
            item: self.$date,
            content: {
                VStack {
                    Text("Destination view")
                    Text($0.debugDescription)
                }
            }
        )
    }
}

저는 최근에 이 문제(양식의 탐색 행 구성 요소)로 어려움을 겪었고, 이것이 저에게 도움이 되었습니다.

@State private var shouldShowDestination = false

NavigationLink(destination: DestinationView(), isActive: $shouldShowDestination) {
    Button("More info") {
        self.shouldShowDestination = true
    }
}

간단히 포장합니다.Button와 함께NavigationLink어떤 활성화를 버튼으로 제어해야 하는지 확인합니다.

이제 활성화하지 않고 동일한 보기에 여러 개의 버튼+링크가 있어야 하는 경우State각 속성, 이 이니셜라이저에 의존해야 합니다.

    /// Creates an instance that presents `destination` when `selection` is set
    /// to `tag`.
    public init<V>(destination: Destination, tag: V, selection: Binding<V?>, @ViewBuilder label: () -> Label) where V : Hashable

https://developer.apple.com/documentation/swiftui/navigationlink/3364637-init

이 예에 따라 다음이 수행됩니다.

struct ContentView: View {
    @State private var selection: String? = nil

    var body: some View {
        NavigationView {
            VStack {
                NavigationLink(destination: Text("Second View"), tag: "Second", selection: $selection) {
                    Button("Tap to show second") {
                        self.selection = "Second"
                    }
                }
                NavigationLink(destination: Text("Third View"), tag: "Third", selection: $selection) {
                    Button("Tap to show third") {
                        self.selection = "Third"
                    }
                }
            }
            .navigationBarTitle("Navigation")
        }
    }
}

자세한 정보(및 위의 약간 수정된 예)는 https://www.hackingwithswift.com/articles/216/complete-guide-to-navigationview-in-swiftui ("프로그램 탐색")에서 가져온 것입니다.

사용자 구성 요소된 " " " " 를 하여 사용자 정의 보기 구성 요소를 합니다.NavigationLink), 예를 들어 이것과 같습니다.

struct FormNavigationRow<Destination: View>: View {

    let title: String
    let destination: Destination

    var body: some View {
        NavigationLink(destination: destination, isActive: $shouldShowDestination) {
            Button(title) {
                self.shouldShowDestination = true
            }
        }
    }

    // MARK: Private

    @State private var shouldShowDestination = false
}

그리고 그것을 반복적으로 일부로 사용합니다.Form(또는)List):

Form {
    FormNavigationRow(title: "One", destination: Text("1"))
    FormNavigationRow(title: "Two", destination: Text("2"))
    FormNavigationRow(title: "Three", destination: Text("3"))
}

.onAppear새 화면이 나타날 때만 실행해야 하는 모든 코드를 입력합니다.다음과 같이:

struct DestinationView: View {
    var body: some View {
        Text("Hello world!")
        .onAppear {
            // Do something important here, like fetching data from REST API
            // This code will only be executed when the view appears
        }
    }
}

나는 재사용 가능한 나 자신을 만들었습니다.LazyNavigationLink코드에서 간단히 대체NavigationLink타고MyLazyNavigationLink

public struct MyLazyNavigationLink<Label: View, Destination: View>: View {
    var destination: () -> Destination
    var label: () -> Label

    public init(@ViewBuilder destination: @escaping () -> Destination,
                @ViewBuilder label: @escaping () -> Label) {
        self.destination = destination
        self.label = label
    }

    public var body: some View {
        NavigationLink {
            LazyView {
                destination()
            }
        } label: {
            label()
        }
    }

    private struct LazyView<Content: View>: View {
        var content: () -> Content
     
        var body: some View {
            content()
        }
    }
}

그것은 실제로 예상되는 행동입니다.은 당은당신서사합니다야용해를비로 .@StateObject東京의 AnotherView.@StateObject가 서스의이서다같비표음경이호시출다우됩니만으로 표시된 합니다.@StateObject("description이 아닌 "으로 표시됨)에됩니다.AnotherView구조는 실제로 존재하며, 이는 화면의 보기 수명 동안 여러 번 재생성될 수 있습니다.

언급URL : https://stackoverflow.com/questions/57594159/swiftui-navigationlink-loads-destination-view-immediately-without-clicking

반응형