How to Specify the Type Information of an Array That Would Hold Swiftui Custom Views

SwiftUI - How to create and use an array of custom types

You cannot perform any operation inside the ForEach. The ForEach expects to have a single View returned from it. But if you want to append data inside the ForEach then you can do that when the onAppear() closure is called on your View which is inside the ForEach.

This is what your code will look like when the elements array is appended in the onAppear of your View.

VStack {
ForEach (0..<26) { index in
Text("\(index) Hello, World!").onAppear() {

elements.append(Element(direction: "Direction \(index)", movement: "Movement \(index)", multiplier: index))
}
}
}

And you are getting the "immutable error" because you're probably declaring your elements variable inside the struct and properties inside the struct are immutable. So to be able to append new values you either need to annotate your elements variable with @State or you can declare it outside of the struct

Outside of struct declaration:

var elements: [Element] = []
struct MyView: View {

}

With annotation but inside struct declaration:

struct MyView: View {
@State var elements: [Element] = []
}

And you also need to know that changing values of @State annotated properties updates the UI but this shouldn't be something to worry about as SwiftUI calculates the changes in the UI and updates only the new changes

How to have a dynamic List of Views using SwiftUI

Looks like the answer was related to wrapping my view inside of AnyView

struct ContentView : View {
var myTypes: [Any] = [View1.self, View2.self]
var body: some View {
List {
ForEach(0..<myTypes.count) { index in
self.buildView(types: self.myTypes, index: index)
}
}
}

func buildView(types: [Any], index: Int) -> AnyView {
switch types[index].self {
case is View1.Type: return AnyView( View1() )
case is View2.Type: return AnyView( View2() )
default: return AnyView(EmptyView())
}
}
}

With this, i can now get view-data from a server and compose them. Also, they are only instanced when needed.

Switching between an array of views in swiftUI

View is just a struct. You can store it in an array or other attribute type and then use it in another Views. AnyView is the good solution if you really want to mix different types of View in one collection.
To dynamically show that collection I like to use ForeEach. You can iterate not the exact Views but indices of an array. Therefore SwiftUI will make a key of this View from an array index of it. But it have a cost. You can't dynamically change the content of that array. it must be constant at all parent View lifetime.
To be honest, you actually can iterate the exact View through the ForeEach but you need to make them Identyfiable (they must have an public var id = UUID() attribute).

import SwiftUI

let dotSize:CGFloat = 24
let diceSize:CGFloat = 128
let fieldSide:CGFloat = 32

struct ContentView: View {
var diceViews:[AnyView] = [AnyView(oneDotDice()), AnyView(twoDotDice())]
@State var showInt: Int = 1
var body: some View {
ZStack{
ForEach(diceViews.indices){ind in
ZStack{
if ind == self.showInt{
self.diceViews[ind]
.transition(.scale(scale: 0, anchor: .center))
.onTapGesture {
let newShow = Int.random(in: 0...1)
print("new show id \(newShow)")
withAnimation(.linear(duration: 0.3)){
self.showInt = newShow
}
}
}
}

}
}
}
}
struct oneDotDice: View {
var text: String = "1"
var body: some View {
Rectangle()
.fill(Color.clear)
.frame(width: diceSize, height: diceSize)
.border(Color.black)
.background(Text(text))
}
}

struct twoDotDice: View {
var text: String = "2"
var body: some View {
Rectangle()
.fill(Color.clear)
.frame(width: diceSize, height: diceSize)
.border(Color.black)
.background(Text(text))
}
}

struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

By the way, I change your random logic, not sure how it was working, but I think its not really important for your question.

Style views passed to custom view in SwiftUI

Like This :-

import SwiftUI

struct ContentView: View {

var body: some View {

HStack {
CustomText(text: "kjahsfgdi f")
Spacer()
CustomText(text: "abc")
Spacer()
CustomText(text: "abc")
}

}
}

struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

struct CustomText : View{
@State var text: String
var body: some View {
Text(text)
.lineLimit(nil)
.frame(width: 100, height: nil, alignment: .center)
}
}

Create a List with an array of custom classes in SwiftUI

You are mixing two separate things. @State is meant to be used for anything internal to the view. While @BindableObject, is meant to hold something external to the view (i.e., your data model). When you define a @BindableObject, you do not reference it with @State, but with @ObjectBinding.

I don't know what was the case, but if you intend to use @State, the implementation should have been like this:

struct Media {

var id: Int
var name: String

init(id: Int, name: String) {
self.id = id
self.name = name
}
}

struct ContentView : View {
@State var media = [
Media(id: 0, name: "Name 0"),
Media(id: 1, name: "Name 1"),
Media(id: 2, name: "Name 2"),
Media(id: 3, name: "Name 3"),
]

var body: some View {
VStack {
List(media.identified(by: \.id)) { media in
Text(media.name)
}

HStack {
Button(action: {
self.media[0].name = "Name \(Int.random(in: 100...199))"
}) {
Text("Change")
}
Button(action: {
self.media.append(Media(id: 4, name: "Name 4"))
}) {
Text("Add")
}
}
}
}
}

You would mostly use @State for some internal state of the view, but also while prototyping. What was your intention with the Media object?


UPDATE

To implement it with a @ObjectBinding:

Supposing you are instantiating your CustomView in the SceneDelegate, you need to pass it the library:

        if let windowScene = scene as? UIWindowScene {

let window = UIWindow(windowScene: windowScene)

let library = MediaLibrary(store: [Media(id: 0, name: "Name 0"),
Media(id: 1, name: "Name 1"),
Media(id: 2, name: "Name 2"),
Media(id: 3, name: "Name 3")])

window.rootViewController = UIHostingController(rootView: ContentView(library: library))
self.window = window
window.makeKeyAndVisible()
}

And the implementation:

struct Media {
let id: Int
var name: String

init(id: Int, name: String) {
self.id = id
self.name = name
}
}

class MediaLibrary: BindableObject {
typealias PublisherType = PassthroughSubject<Void, Never>
var didChange = PublisherType()

var store: [Media] {
didSet {
didChange.send()
}
}

init(store: [Media]) {
self.store = store
}
}

struct ContentView : View {
@ObjectBinding var library: MediaLibrary

var body: some View {
VStack {
List(self.library.store.identified(by: \.id)) { media in
Text(media.name)
}
HStack {
Button(action: {
self.library.store[0].name = "Name \(Int.random(in: 100...199))"
}) {
Text("Change")
}
Button(action: {
self.library.store.append(Media(id: 4, name: "Name 4"))
}) {
Text("Add")
}
}
}
}
}


Related Topics



Leave a reply



Submit