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:
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
How to Use List Type with Codable? (Realmswift)
Swift - Avaudioplayer, Sound Doesn't Play Correctly
How Replace Position++ Code to Make It Swift 3 Compatible
How to Create Several Cached Uicolor
Missing Return in a Function Expected to Return 'Double'
How to Create Enum with Raw Type of Cgpoint
Swift Function with Args... Pass to Another Function with Args
Swift/Uiview/Drawrect - How to Get Drawrect to Update When Required
Swift 3 (Spritekit): Reseting the Gamescene After the Game Ends
Swiftui Foreach Based on State Int
How to Change Navigation Bar & Back Button Colour iOS 15
Extending Collection with a Recursive Property/Method That Depends on the Element Type
How to Make Physics Bodies Stick to Nodes Anchor Points
Call Completion Block When Two Other Completion Blocks Have Been Called
Compare App Versions After Update Using Decimals Like 2.5.2