Swiftui List Empty State View/Modifier

SwiftUI list empty state view/modifier

One of the solutions is to use a @ViewBuilder:

struct EmptyListExample: View {
var objects: [Int]

var body: some View {
listView
}

@ViewBuilder
var listView: some View {
if objects.isEmpty {
emptyListView
} else {
objectsListView
}
}

var emptyListView: some View {
Text("Oops, loos like there's no data...")
}

var objectsListView: some View {
List(objects, id: \.self) { obj in
Text("\(obj)")
}
}
}

How to display a text message at the Center of the view when the List datasource is empty in SwiftUI?

struct LandmarkList: View {
var body: some View {
NavigationView {
if landmarkData.count == 0 {
VStack {
Text("is empty")
} else {
List(landmarkData) { landmark in
LandmarkRow(landmark: landmark)
}
}
}
}
}

An if statement that shows text Your list is empty if the List view is empty

You can simply use an if statement inside your view, like this:

            Text("My List")
.font(.title3).bold()
.foregroundColor(.black)
.padding()
.frame(maxWidth: .infinity, alignment: .leading)

// Here is the condition:
if RealmManager.tasks.isEmpty {
Text("Your list is empty")
} else {

List {
ForEach(RealmManager.tasks, id: \.id) {
task in
if !task.isInvalidated {
TaskRow(task: task.title, completed: task.completed)
.onTapGesture {
RealmManager.updateTask(id: task.id, completed: !task.completed)
}

Display a View when rest of content is empty

Here's one implementation using GeometryReader & it's named emptyState:

extension View {
func emptyState<Content: View>(@ViewBuilder content: () -> Content) -> some View {
return self.modifier(EmptyStateModifier(placeHolder: content()))
}
}

struct EmptyStateModifier<PlaceHolder: View>: ViewModifier {
@State var isEmpty = false
let placeHolder: PlaceHolder
func body(content: Content) -> some View {
ZStack {
if isEmpty {//Thanks to @Asperi
placeHolder
}
content
.background(
GeometryReader { reader in
Color.clear
.onChange(of: reader.frame(in: .global).size == .zero) { newValue in
isEmpty = reader.frame(in: .global).size == .zero
}
}
)
}
}
}

PieChart in swiftui is not rendered when a member has @State modifier

First of all don't use _ in var names. You only need them to access underlying values e.g. in the init.

Second I'm confused about your mention of class as I don't see any in your code.

I would put the PieView in an extra view that only does that: displaying a pie based on data handed over.

This data and the colors don't have to be state inside the view. Only things that can change inside the view have to be @State. Your slicePressedNo might have to be @Binding as you would detect this inside the view on tap. All other can be simple vars or even lets.

So the PieView could look like this:

struct PieView: View {

var data: [Float]
var colors: [Color]
@Binding var slicePressedNo: Int

private var angles: [Float] = []
private var offsets: [Float] = []
var total: Float = 0
private var sortedData: [Float]
private var percentages: [Float] = []

init (data: [Float], colors: [Color], slicePressedNo: Binding<Int>) {
self.data = data
self.colors = colors
self._slicePressedNo = slicePressedNo // only _ here
self.sortedData = data

// ... your code following

Then the calling view can use @State for changing values, which it passes down to PieView. Once these values change, PieView will be redrawn.

Here is a simple example which adds new values on button press:

struct ContentView: View {

@State private var pressed = 1
@State private var data: [Float] = [1, 5, 3, 8]
@State private var colors: [Color] = [.blue, .red, .green, .orange, .teal]

var body: some View {
VStack {
PieView(data: data,
colors: colors,
slicePressedNo: $pressed)

Button("Add slice") {
colors.append(colors.randomElement() ?? .red)
data.append(Float.random(in: 0..<15))
}
}
}
}

Here is the result:

Sample Image

Can't use State var within sheet modifier SwiftUI?

You can present a sheet by toggling a boolean

func sheet<Content>(isPresented: Binding<Bool>,...

or by selecting an item

func sheet<Item, Content>(item: Binding<Item?>,...

but not both simultaneously. If you want to proceed with the selected item use only the latter form and delete the Bool

struct PlayerListingView: View {

@State private var selectedPlayer: PlayerEntity?

var body: some View {
Section {
ForEach(players) { player in
playerCell(player, onSelect: { p in
selectedPlayer = p
printv("cell selected selectedPlayer: \(selectedPlayer)")

})
}
}
.sheet(item: $selectedPlayer) {
printv("selectedPlayer: \(selectedPlayer)")
EditPlayerView(player: selectedPlayer)
}
}

SwiftUI ViewModifier not working as a NavigationLink

The modifier doesn't work because the content argument is not the actual view being modified, but instead is a proxy:

content is a proxy for the view that will have the modifier represented by Self applied to it.

Reference.

This is what a quick debugging over the modifier shows:

(lldb) po content
SwiftUI._ViewModifier_Content<SwiftUIApp.NavLink>()

As the proxy is an internal type of SwiftUI, we can't know for sure why NavigationLink doesn't work with it.

A workaround would be to skip the modifier, and only add the extension over View:

extension View {
func navLink(title: String) -> some View {
NavigationLink(destination: content) {
Text(title)
}
}
}

SwiftUI: @State property not updated without weird workaround

class ObservationSession: //Codable, //implement Codable manually
ObservableObject {
public let id: UUID
//This allows you to observe the individual variable
@Published public var name: String

public init(name: String) {
self.name = name
self.id = UUID()
}

}

struct SessionListModals {
enum Flow: Identifiable {
case configuration
case observation
case newSession

var id: Flow { self }
}
}

// ContentView
class ContentViewModel: ObservableObject {
@Published var mutableSession: ObservationSession?

}
struct ContentView: View {
//State stores the entire object and observes it as a whole it does not individually observe its variables that is why .onChange works
@StateObject var vm: ContentView3Model = ContentView3Model()
@State private var flow: SessionListModals.Flow?
var body: some View {
VStack {
Button("New Session", action: {
//Since you want to change it programatically you have to put them in another object
vm.mutableSession = ObservationSession(name: "")
flow = .newSession
})
.padding()
}
.fullScreenCover(item: $flow) {
viewForFlow($0)
}
}

@ViewBuilder private func viewForFlow(_ flow: SessionListModals.Flow) -> some View {
switch flow {
case .newSession:
// MARK: - Show New Session View
NavigationView {
NewSessionView(session: $vm.mutableSession, flow: $flow)
.navigationTitle("Create a session")
.navigationBarItems(leading: Button("Cancel", action: {
self.flow = nil
}))
}
case .observation:
// MARK: - Show RecordingView
NavigationView {
let name = vm.mutableSession?.name ?? "Unnamed session"
RecordingView(sessionName: name)
.navigationBarItems(leading: Button("Close", action: {
self.flow = nil
}))
}
default:
NavigationView {
EmptyView()
.navigationBarItems(leading: Button("Close", action: {
self.flow = nil
}))
}
}
}
}


Related Topics



Leave a reply



Submit