SwiftUI WidgetKit: How to anchor views to the top

筅森魡賤 提交于 2021-02-07 10:00:22

问题


I am attempting to layout a tableView using SwiftUI and WidgetKit and would like to achieve a similar result of that as the Apple's Notes widget.

My current implementation succeeds in laying out the view in the .systemLarge widget, but not in the .systemMedium widget. I would like to pin the view to the top of the widget, such that the header of "FAVOURITES" is visible in the .systemMedium.

struct PlacesWidgetEntryView : View {
    var entry: Provider.Entry

    let places = [
        Place(name: "Place 1", imageName: "baseline_star_black_24pt"),
        Place(name: "Place 2", imageName: "baseline_star_black_24pt"),
        Place(name: "Place 3", imageName: "baseline_star_black_24pt"),
        Place(name: "Place 4", imageName: "baseline_star_black_24pt"),
        Place(name: "Place 5", imageName: "baseline_star_black_24pt"),

    ]
    
    var body: some View {
        VStack {
            //Header
            HStack {
                Text("FAVOURITES")
                    .bold()
                    .frame(height: 8)
                Spacer()
            }
            .padding()
            .background(Color.blue)

            //TableView
            LazyVStack {
                ForEach(places, id: \.self) { place in
                    PlaceRow(place: place)
                }
            }
            Spacer()
        }
    }
}

struct PlaceRow: View {
    let place: Place
    
    var body: some View {
        HStack {
            Text(place.name)
                .font(.body)
            Spacer()
            Image(place.imageName)
                .resizable()
                .frame(width: 28, height: 28, alignment: .center)
        }
        .padding(.horizontal)
        .padding(.vertical, 4)
    }
}

Implementation outcome:

The above is .systemLarge, which is good, and as per what I'm expecting.

The above is .systemMedium, which is not what I'm expecting. I would like to see "Favourites" anchored to the top of the widgetView, and potentially the tableView overflowing to the bottom.


回答1:


Here is possible layout solution. Tested with Xcode 12.

var body: some View {
    VStack(spacing: 0) {
        HStack {
            Text("FAVOURITES")
                .bold()
                .frame(height: 8)
            Spacer()
        }
        .padding()
        .background(Color.blue)
        
        Color.clear
            .overlay(
                LazyVStack {
                    ForEach(places, id: \.self) { place in
                        PlaceRow(place: place)
                    }
                },
                alignment: .top)
    }
    .frame(maxWidth: .infinity, maxHeight: .infinity)
}



回答2:


So from playing around in WidgetKit, it seems like if there are too many views in a widget, it starts to push the upper views off the screen. If you add more places to the array, you'll see the same thing happen with your large widget. What you can do is create separate views: one for the medium and one for the large widget, and for the medium one, just use 1-3 of your place objects to populate it.

You can use a switch statement in your PlacesEntryWidgetView with the widgetFamily to decide what you want to show on the view. I also slightly reduced the height of the image from 28 to 24.

struct PlacesWidgetEntryView : View {
    var entry: Provider.Entry
    @Environment(\.widgetFamily) var family
    
    let places = [
            Place(name: "Place 1", imageName: "blackStar"),
            Place(name: "Place 2", imageName: "blackStar"),
            Place(name: "Place 3", imageName: "blackStar"),
            Place(name: "Place 4", imageName: "blackStar"),
            Place(name: "Place 5", imageName: "blackStar"),
            Place(name: "Place 6", imageName: "blackStar"),
            Place(name: "Place 7", imageName: "blackStar"),
            Place(name: "Place 8", imageName: "blackStar")
        ]

    @ViewBuilder
    var body: some View {
        switch family {
        case .systemMedium:
            // widget can only show so many views so I only took first 3 places
            WidgetView(places: Array(places.prefix(3)))
        case .systemLarge:
            WidgetView(places: places)
        // I only have it set to show system medium so you can ignore
        // the last case
        case .systemSmall:
            Text("")
        @unknown default:
            Text("")
        }
    }
}

struct WidgetView: View {
    let places: [Place]
    
    var body: some View {
        VStack {
            //Header
            HStack {
                Text("FAVOURITES")
                    .bold()
                    .frame(height: 8)
                Spacer()
            }
            .padding()
            .background(Color.blue)

            //TableView
            LazyVStack {
                ForEach(places, id: \.self) { place in
                    PlaceRow(place: place)
                }
            }
            Spacer()
        }
    }
}

struct PlaceRow: View {
    let place: Place
    
    var body: some View {
        HStack {
            Text(place.name)
                .font(.body)
            Spacer()
            Image(place.imageName)
                .resizable()
                .frame(width: 28, height: 24, alignment: .center)
        }
        .padding(.horizontal)
        .padding(.vertical, 4)
    }
}

This is the preview:

Keep in mind, that you might want to switch between the simulators to make sure your widgets look good on all devices.



来源:https://stackoverflow.com/questions/64290297/swiftui-widgetkit-how-to-anchor-views-to-the-top

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!