I am creating a widget for my application.
For the large size of the widget, I want to show 4 cells with different content that I fetch from an API call. But I couldn't manage to do it. This is how my widget looks like currently:
Should I create a different entries struct for every cell? Or is there a way to put array/list in the entry struct and append 4 data I got from the request?
Any suggestions about how can I achieve this?
My entry struct:
struct NewsEntry: TimelineEntry {
var date: Date
let configuration: ConfigurationIntent
let header: String
let imageUrl: String
}
My getTimeline function:
func getTimeline(for configuration: ConfigurationIntent, in context: Context, completion: #escaping (Timeline<Entry>) -> ()) {
var entries: [NewsEntry] = []
let currentDate = Date()
let later = Calendar.current.date(byAdding: .minute, value: 5, to: Date())!
NewsProvider.request() { (data, error) in
guard let data = data else {
if let error = error {
print(error)
}
return
}
let entry = NewsEntry(date: currentDate, configuration: configuration, header: data[0].title!, imageUrl: data[0].imageUrlDetail!)
entries.append(entry)
let timeline = Timeline(entries: entries, policy: .after(later))
completion(timeline)
}
}
My EntryView:
struct WidgetEntryView : View {
var entry: Provider.Entry
#Environment(\.widgetFamily)
var widgetFamily
var body: some View {
VStack(alignment: .trailing, spacing: 6) {
Image("logo")
.frame(maxWidth: .infinity, alignment: .leading)
if widgetFamily == .systemLarge {
var columns: [GridItem] =
Array(repeating: .init(), count: 2)
LazyVGrid(columns: columns) {
ForEach((0..<4)) { index in
ZStack (alignment: .bottomLeading) {
if let url = URL(string: entry.imageUrl), let imageData = try? Data(contentsOf: url),
let uiImage = UIImage(data: imageData) {
Image(uiImage: uiImage)
.centerCropped()
.frame(maxHeight: 150, alignment: .center)
.cornerRadius(10)
.overlay(RoundedRectangle(cornerRadius: 10)
.stroke(Color.gray, lineWidth: 1))
.shadow(radius: 10)
} else {
Image("ph_background")
.centerCropped()
.frame(maxHeight: 150, alignment: .center)
.cornerRadius(10)
.overlay(RoundedRectangle(cornerRadius: 10)
.stroke(Color.gray, lineWidth: 1))
.shadow(radius: 10)
}
Text(entry.header)
.font(.system(size: 12))
.foregroundColor(.white)
.fontWeight(.light)
// .frame(maxHeight: 50)
.background(Rectangle().fill(Color.black).blur(radius: 20))
.padding(.bottom, 5)
.padding(.leading, 5)
.padding(.trailing, 5)
.padding(.top, 5)
}
}
.frame(height: 160)
}
}
}
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
.padding()
.background(LinearGradient(gradient: Gradient(colors: [Color(red:0.2, green:0.2, blue:0.2), .black]), startPoint: .top, endPoint: .bottom))
}
}
Changed the variables to [String] in the TimelineEntry and I achieved what I want.
I updated my struct and entry line like this:
var newsList: [String] = [] // First, appending the fetched data here
var imageList: [String] = []
struct NewsEntry: TimelineEntry {
var date: Date
let configuration: ConfigurationIntent
let header: [String]
let imageUrl: [String]
}
for index in 0 ..< 5 {
newsList.append(data[index].title!)
imageList.append(data[index].imageUrlDetail!)
}
// Then using passing the arrays to TimelineEntry as [String]
for hourOffset in 0 ..< 5 {
let entryDate = Calendar.current.date(byAdding: .minute, value: hourOffset, to: currentDate)!
let entry = NewsEntry(date: entryDate, configuration: configuration, header: newsList, imageUrl: imageList)
entries.append(entry)
}
The only problem right now is I don't know if I'm using it correctly because I get crash with "Index out of range" on entry.imageUrl[index]. This is the LazyVGrid my updated View function:
LazyVGrid(columns: columns) {
ForEach((0..<4)) { index in
ZStack (alignment: .bottomLeading) {
if let url = URL(string: entry.imageUrl[index]), let imageData = try? Data(contentsOf: url),
let uiImage = UIImage(data: imageData) { // Thread 1: Fatal error: Index out of range
Image(uiImage: uiImage)
.centerCropped()
.frame(maxHeight: 150, alignment: .center)
.cornerRadius(10)
.overlay(RoundedRectangle(cornerRadius: 10)
.stroke(Color.gray, lineWidth: 1))
.shadow(radius: 10)
} else {
Image("ph_background")
.centerCropped()
.frame(maxHeight: 150, alignment: .center)
.cornerRadius(10)
.overlay(RoundedRectangle(cornerRadius: 10)
.stroke(Color.gray, lineWidth: 1))
.shadow(radius: 10)
}
Text(entry.header[index])
.font(.system(size: 12))
.foregroundColor(.white)
.fontWeight(.light)
// .frame(maxHeight: 50)
.background(Rectangle().fill(Color.black).blur(radius: 20))
.padding(.bottom, 5)
.padding(.leading, 5)
.padding(.trailing, 5)
.padding(.top, 5)
}
}
.frame(height: 160)
}
Related
when i pick an image using Image Picker from my Quick add View it kills all previous views and take me to the HomePage Tab which is Meal Planner View.Sometimes it works normal only on IOS device. It never works fine on simulator as i tried different simulators in Xcode. It also works fine in simulator and IOS device when i remove my TabBar and run directly from Meal Planner View.
kindly suggest something helpful.
import SwiftUI
struct QuickAddView: View {
#Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
var viewName: String
#ObservedObject var vm = MealViewModel()
#State private var showPicker = false
#State private var selectedImage: UIImage?
#State var mealTitle: String = ""
#State var text: String = ""
// #State var descrption: String = ""
var body: some View {
VStack{
navBarView()
ScrollView(showsIndicators: false){
VStack {
VStack(alignment: .leading) {
Button {
self.showPicker.toggle()
} label: {
if selectedImage == nil {
ZStack {
Image("qorma")
.resizable()
.frame( height: 100)
HStack{
Text("+").font(.custom(Nunito.Bold.rawValue, size: 22))
.foregroundColor(.white)
Text("Attach a Photo").font(.custom(Nunito.Regular.rawValue, size: 20))
.foregroundColor(.white)
}
}
}
else {
ZStack {
Image(uiImage: selectedImage!)
.resizable()
.frame( height: 100)
HStack{
Text("+").font(.custom(Nunito.Bold.rawValue, size: 22))
.foregroundColor(.white)
Text("Attach a Photo").font(.custom(Nunito.Regular.rawValue, size: 20))
.foregroundColor(.white)
}
}
}
}
}
CustomTextField(text: $vm.title, placeHolder: "Name")
.background(.white)
.padding([.vertical],30)
fields()
TextEditor(text: $vm.description)
.textFieldStyle(.roundedBorder)
.colorMultiply(.white)
.frame( height: 199)
.border(.gray, width: 0.5)
.cornerRadius(10)
.shadow(radius: 1)
.foregroundColor(.gray)
.padding([.top],30)
addButton()
}
}
}.padding()
.sheet(isPresented: $showPicker) {
ImagePicker(image: $selectedImage)
}
}
}
extension QuickAddView {
func nutrientFields(nutrientTitle: String,amount: Binding<String>) -> some View {
VStack(alignment: .leading){
HStack(){
Text(nutrientTitle)
.font(.custom(Nunito.Semibold.rawValue, size: 24))
.foregroundColor(.gray)
Spacer()
HStack{
ZStack{
TextField("0", text: amount)
.font(Font.system(size: 30))
.background(Color.white)
.foregroundColor(Color(ColorName.appAqua.rawValue))
.frame(width: 70)
}
.background(
Rectangle()
.foregroundColor(Color.white)
.opacity(0.2)
)
.overlay(
RoundedRectangle(cornerRadius: 5)
.stroke(Color(ColorName.appAqua.rawValue), lineWidth: 2.5)
)
Text("(g)")
.font(.custom(Nunito.Semibold.rawValue, size: 30))
.foregroundColor(Color(ColorName.appAqua.rawValue))
}.padding([.horizontal])
// .font(.custom(Nunito.Semibold.rawValue, size: 20))
// .foregroundColor(Color(ColorName.appAqua.rawValue))
// .frame(width: 40, height: 30)
}
}
}
func calorieField (nutrientTitle: String,amount: Binding<String>) -> some View {
VStack(alignment: .leading){
HStack(){
Text(nutrientTitle)
.font(.custom(Nunito.Semibold.rawValue, size: 24))
.foregroundColor(.gray)
Spacer()
HStack{
ZStack{
TextField("0", text: amount)
.font(Font.system(size: 30))
.background(Color.white)
.foregroundColor(Color(ColorName.appAqua.rawValue))
.frame(width: 70)
}
.background(
Rectangle()
.foregroundColor(Color.white)
.opacity(0.2)
)
.overlay(
RoundedRectangle(cornerRadius: 5)
.stroke(Color(ColorName.appAqua.rawValue), lineWidth: 2.5)
)
}.padding([.horizontal],59)
}
}
}
func fields() -> some View {
VStack(spacing: 20){
calorieField(nutrientTitle: "Calories",amount: $vm.cals)
Rectangle()
.fill(.gray)
.frame( height: 1)
nutrientFields(nutrientTitle: "Carbohydrates",amount: $vm.carb)
Rectangle()
.fill(.gray)
.frame( height: 1)
nutrientFields(nutrientTitle: "Proteins",amount: $vm.aminos)
Rectangle()
.fill(.gray)
.frame( height: 1)
nutrientFields(nutrientTitle: "Fats",amount: $vm.lipids)
}
}
func navBarView() -> some View {
ZStack {
HStack{
Button ( action: { self.presentationMode.wrappedValue.dismiss() },
label: {
Image(systemName: "arrow.backward").frame(width: 16, height: 16)
.foregroundColor(Color(ColorName.appAqua.rawValue))
})
Spacer()
}
Text("Quick Add")
.font(.custom(Nunito.Bold.rawValue, size: 22.5))
.foregroundColor(Color(ColorName.appAqua.rawValue))
}
}
func addButton() -> some View {
// NavigationLink(
// destination: BreakFastMainView(viewName: viewName, firebaseId: UserDefaults.standard.value(forKey: "FireBaseId") as! String) .navigationBarTitle("")
// .navigationBarHidden(true)
// ,
Button {
vm.addMealData(category: viewName, image: selectedImage ?? UIImage(), name: vm.title, cals: vm.cals, carbs: vm.carb, protein: vm.aminos, fat: vm.lipids, description: vm.description, firebaseId: UserDefaults.standard.value(forKey: "FireBaseId") as? String ?? "12345", action: {
self.presentationMode.wrappedValue.dismiss()
})
}
label: {
ZStack {
RoundedRectangle(cornerRadius: 10)
.frame(height: 50)
.foregroundColor(Color("btnBlue"))
Text("Add")
.font(.custom("Nunito-Bold", size: 20))
.foregroundColor(.white)
}.padding([.top], 32)
}
}
func data(_ name: String, _ amount: String) -> some View {
ZStack(alignment: .leading) {
RoundedRectangle(cornerRadius: 5)
.stroke( .gray, lineWidth: 0.5)
.frame(height: 56)
HStack {
Text(name)
.foregroundColor( .gray)
.font(.custom(Nunito.Semibold.rawValue, size: 20))
Spacer()
Text(amount)
.foregroundColor( Color(ColorName.appAqua.rawValue))
.font(.custom(Nunito.Semibold.rawValue, size: 20))
Text("g")
.foregroundColor( Color(ColorName.appAqua.rawValue))
.font(.custom(Nunito.Semibold.rawValue, size: 20))
}.padding()
}
}
}
struct QuickAddView_Previews: PreviewProvider {
static var previews: some View {
QuickAddView( viewName: "dfd")
}
}
When I make dropdown list menu in SwiftUi, dropdown list shows behind of other Component View, I've tried to zIndex(1) at the last of the VStack, and I've tried .overlay at the top of the Stack but it didn't solve my problem, I've shared below code and I've shared screenshot of the problem, how can I solve this problem? thanks...
import SwiftUI
struct DropdownOption: Hashable {
let key: String
let value: String
public static func == (lhs: DropdownOption, rhs: DropdownOption) -> Bool {
return lhs.key == rhs.key
}
}
struct DropdownRow: View {
var option: DropdownOption
var onOptionSelected: ((_ option: DropdownOption) -> Void)?
var body: some View {
Button(action: {
if let onOptionSelected = self.onOptionSelected {
onOptionSelected(self.option)
}
}) {
HStack {
Text(self.option.value)
.font(.system(size: 14))
.foregroundColor(Color.black)
Spacer()
}
}
.padding(.horizontal, 16)
.padding(.vertical, 5)
}
}
struct Dropdown: View {
var options: [DropdownOption]
var onOptionSelected: ((_ option: DropdownOption) -> Void)?
var body: some View {
ScrollView {
VStack(alignment: .leading, spacing: 0) {
ForEach(self.options, id: \.self) { option in
DropdownRow(option: option, onOptionSelected: self.onOptionSelected)
}
}
}
.frame(minHeight: CGFloat(options.count) * 30, maxHeight: 250)
.padding(.vertical, 5)
.background(Color.white)
.cornerRadius(5)
.overlay(
RoundedRectangle(cornerRadius: 5)
.stroke(Color.gray, lineWidth: 1)
)
}
}
struct DropdownSelector: View {
#State private var shouldShowDropdown = false
#State private var selectedOption: DropdownOption? = nil
var placeholder: String
var options: [DropdownOption]
var onOptionSelected: ((_ option: DropdownOption) -> Void)?
private let buttonHeight: CGFloat = 45
var body: some View {
Button(action: {
self.shouldShowDropdown.toggle()
}) {
HStack {
Text(selectedOption == nil ? placeholder : selectedOption!.value)
.font(.system(size: 14))
.foregroundColor(selectedOption == nil ? Color.gray: Color.black)
Spacer()
Image(systemName: self.shouldShowDropdown ? "arrowtriangle.up.fill" : "arrowtriangle.down.fill")
.resizable()
.frame(width: 9, height: 5)
.font(Font.system(size: 9, weight: .medium))
.foregroundColor(Color.black)
}
}
.padding(.horizontal)
.cornerRadius(5)
.frame(width: .infinity, height: self.buttonHeight)
.overlay(
RoundedRectangle(cornerRadius: 5)
.stroke(Color.gray, lineWidth: 1)
)
.overlay(
VStack {
if self.shouldShowDropdown {
Spacer(minLength: buttonHeight + 10)
Dropdown(options: self.options, onOptionSelected: { option in
shouldShowDropdown = false
selectedOption = option
self.onOptionSelected?(option)
})
}
}, alignment: .topLeading
)
.background(
RoundedRectangle(cornerRadius: 5).fill(Color.white)
)
}
}
calling DropDownList
ZStack(alignment:.top){
HStack {
Group {
DropdownSelector(
placeholder: "Choose Aircraft Type",
options: options,
onOptionSelected: { option in
print(option)
})
.padding(.horizontal)
}
}.padding(.top, 50)
HStack {
Group {
DropdownSelector(
placeholder: "Choose Simulator Type",
options: optionsSimulator,
onOptionSelected: { option in
print(option)
})
.padding(.horizontal)
}
}.padding(.top, 50)
}
I'm using a 4-block grid for my Widget Extension.
I want to populate these grids with an array of elements.
let exampleArray = ["test1","test2","test3","test4"]
When I use two nested ForEach loops, I can't get the element in the 4th index.
VStack {
ForEach((0..<2)) { column in
HStack {
ForEach((0..<2)) { index in
ZStack (alignment: .bottomLeading) {
Text(newsList[(index+column)]) // 0+0, 1+0, 0+1, 1+1
// Text = test1, test2, test2, test3
}
}
}
}
}
Any way I can work around this? Because it's in View, I can't use any operations.
Full code of the View:
struct AppWidgetEntryView : View {
var entry: Provider.Entry
#Environment(\.widgetFamily)
var widgetFamily
var body: some View {
VStack(alignment: .trailing, spacing: 6) {
Image("logo")
.frame(maxWidth: .infinity, alignment: .leading)
if widgetFamily == .systemLarge {
VStack {
ForEach((0..<2)) { column in
HStack {
ForEach((0..<2)) { index in
ZStack (alignment: .bottomLeading) {
if let url = URL(string: imageList[(index+column)]), let imageData = try? Data(contentsOf: url),
let uiImage = UIImage(data: imageData) {
Image(uiImage: uiImage)
.centerCropped()
.frame(maxHeight: 150, alignment: .center)
.cornerRadius(10)
.overlay(RoundedRectangle(cornerRadius: 10)
.stroke(Color.gray, lineWidth: 1))
.shadow(radius: 10)
} else {
Image("ph_background")
.centerCropped()
.frame(maxHeight: 150, alignment: .center)
.cornerRadius(10)
.overlay(RoundedRectangle(cornerRadius: 10)
.stroke(Color.gray, lineWidth: 1))
.shadow(radius: 10)
}
Text(newsList[(index+column)])
.font(.system(size: 12))
.foregroundColor(.white)
.fontWeight(.light)
// .frame(maxHeight: 50)
.background(Rectangle().fill(Color.black).blur(radius: 20))
.padding(.bottom, 5)
.padding(.leading, 5)
.padding(.trailing, 5)
.padding(.top, 5)
}
}
}
}
}
}
}
}
Solution
Please check the accepted answer.
struct AppWidgetEntryView : View {
var entry: Provider.Entry
#Environment(\.widgetFamily)
var widgetFamily
var body: some View {
var columns: [GridItem] =
Array(repeating: .init(.fixed(100)), count: 2)
if widgetFamily == .systemLarge {
LazyVGrid(columns: columns) {
ForEach((0..<4)) { index in
ZStack (alignment: .bottomLeading) {
if let url = URL(string: imageList[(index)]), let imageData = try? Data(contentsOf: url),
let uiImage = UIImage(data: imageData) {
Image(uiImage: uiImage)
.centerCropped()
.frame(maxHeight: 150, alignment: .center)
.cornerRadius(10)
.overlay(RoundedRectangle(cornerRadius: 10)
.stroke(Color.gray, lineWidth: 1))
.shadow(radius: 10)
} else {
Image("ph_background")
.centerCropped()
.frame(maxHeight: 150, alignment: .center)
.cornerRadius(10)
.overlay(RoundedRectangle(cornerRadius: 10)
.stroke(Color.gray, lineWidth: 1))
.shadow(radius: 10)
}
Text(newsList[(index)])
.font(.system(size: 12))
.foregroundColor(.white)
.fontWeight(.light)
// .frame(maxHeight: 50)
.background(Rectangle().fill(Color.black).blur(radius: 20))
.padding(.bottom, 5)
.padding(.leading, 5)
.padding(.trailing, 5)
.padding(.top, 5)
}
}
}
}
}
}
Use LazyVGrid
I feel like you really should be using LazyVGrid for this...
https://developer.apple.com/documentation/swiftui/lazyvgrid
It works like a List but it places each item in a grid of columns.
Using this will solve this problem for you.
Without LazyVGrid
If you really don't want to use a LazyVGrid (please use this though). Then the calculation you are using is not correct. The calculation is something like...
index = row*numberOfCols + column
You can't just use the row as that will only increase the index by 1 per row. But you need to increase the index by the number of columns per row (2 in your case).
I have a simple Map view:
struct MapContainer: View {
#EnvironmentObject var store: AppStore
#State private var region: MKCoordinateRegion = Location.defaultRegion
var events: [Event] {
store.state.getEventsFromSearchResults()
}
func select(id: UUID) {}
var body: some View {
Map(
coordinateRegion: $region,
interactionModes: .all,
showsUserLocation: true,
annotationItems: events,
annotationContent: { event in
MapAnnotation(coordinate: event.coordinates) {
Image("MapMarker")
.resizable()
.scaledToFit()
.frame(width: 24)
.foregroundColor(
event.id == store.state.searchState.tapped
? Color("Accent")
: .black
)
.onTapGesture { select(id: event.id) }
}
}
)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.onAppear { region = store.state.searchState.near.region }
}
}
And I have a screen that uses the above container:
struct MapScreen: View {
let height: CGFloat? = nil
var body: some View {
GeometryReader { geo2 in
ZStack(alignment: .top) {
VStack {
MapContainer()
.animation(.easeOut)
}
.frame(height: height != nil ? height : geo2.size.height / 2 + geo2.safeAreaInsets.top)
VStack(spacing: 0) {
VStack(spacing: 0) {
HStack {
Image(systemName: "minus")
.font(.system(size: 40))
.foregroundColor(Color("LightGray"))
.padding(.vertical, 10)
}
.frame(maxWidth: .infinity)
.contentShape(Rectangle())
MapList()
}
.frame(height: 500)
.background(Color.white.shadow(color: .gray, radius: 5, x: 0, y: 3))
}
.frame(maxHeight: .infinity, alignment: .bottom)
.animation(.easeOut)
}
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .bottom)
.edgesIgnoringSafeArea(.vertical)
}
}
}
The above crashes with - Thread 1: EXC_BAD_ACCESS (code=1, address=0x38) - but if I assign a hard coded value to the MapContainer e.g.
MapContainer().frame(height: 400)
Then everything works fine. Anyone know what's causing the issue?
Forgive me as I am extremely novice with SwiftUI... I am attempting to pull data from the CMS and put it in my app however it is throwing three errors on each attempt for the data to be retrieved and placed...
The errors are highlighting in the sections that read "api.beers.title", "api.beers.type" and "api.beers.description".
Errors
Value of type 'API' has no dynamic member 'beers' using key path from root type 'API'
Referencing subscript 'subscript(dynamicMember:)' requires wrapper 'ObservedObject.Wrapper'
Initializer 'init(_:)' requires that 'Binding' conform to 'StringProtocol'
API Call Code
func getArray(id: String, completion: #escaping([Entry]) -> ()) {
let query = Query.where(contentTypeId: id)
client.fetchArray(of: Entry.self, matching: query) { result in
switch result {
case .success(let array):
DispatchQueue.main.async {
completion(array.items)
}
case .failure(let error):
print(error)
}
}
}
class API: ObservableObject {
#Published var draft: [Draft] = draftData
init() {
getArray(id: "beers") { (items) in
items.forEach { (item) in
self.draft.append(Draft(
title: item.fields["title"] as! String,
type: item.fields["type"] as! String,
description: item.fields["type"] as! String
))
}
}
}
}
struct LandingPageView: View {
var body: some View {
VStack {
VStack {
Text("Problem Solved")
Text("Brewing Company")
}
.font(.system(size: 24, weight: .bold))
.multilineTextAlignment(.center)
.foregroundColor(Color("TextColor"))
VStack {
Text("NEWS & EVENTS")
.font(.title)
.fontWeight(.bold)
.padding(.top, 40)
.foregroundColor(Color("TextColor"))
NewsTile()
Text("On Draft" .uppercased())
.font(.title)
.fontWeight(.bold)
.padding(.top)
.foregroundColor(Color("TextColor"))
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 20) {
ForEach(draftData) { item in
GeometryReader { geometry in
DraftList(beer: item)
.rotation3DEffect(Angle(degrees: Double(geometry.frame(in: .global).minX - 30) / -20), axis: (x: 0, y: 10.0, z: 0))
}
.frame(width: 275, height: 200)
}
}
.padding(.leading, 30)
.padding(.trailing, 30)
}
}
.frame(width: 400, height: 850)
.background(Color("PageBackground"))
.edgesIgnoringSafeArea(.all)
}
}
}
struct DraftList: View {
var width: CGFloat = 275
var height: CGFloat = 200
#ObservedObject var api = API()
var beer: Draft
var body: some View {
VStack {
Spacer()
Text(api.beers.title)
.font(.system(size: 24, weight: .bold))
.padding(.horizontal, 20)
.frame(width: 275, alignment: .leading)
.foregroundColor(Color("TextColor"))
Text(api.beers.type .uppercased())
.font(.system(size: 14, weight: .bold))
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.horizontal, 20)
Text(api.beers.description)
.font(.system(size: 12))
.padding(.horizontal, 20)
.padding(.top, 10)
Spacer()
HStack {
// Add OnTapGesture to bring to full view + cart options.
Text("Click To Add To Cart")
.font(.footnote)
Image(systemName: "cart")
}
.padding(.bottom)
}
.padding(.horizontal, 20)
.frame(width: width, height: height)
.background(Color("TileOrangeColor"))
.cornerRadius(15)
.shadow(color: Color.black.opacity(0.2), radius: 5, x: 0, y: 5)
}
}
Your draft is array, you can access by index like Text(api.draft[0].title)
#Published var draft: [Draft] = draftData instead #Published var draft: [Draft] = []
Updated:
class API: ObservableObject {
#Published var draft: [Draft] = []
init() {
getArray(id: "beers") { (items) in
items.forEach { (item) in
self.draft.append(Draft(
title: item.fields["title"] as! String,
type: item.fields["type"] as! String,
description: item.fields["type"] as! String
))
}
}
}
}
struct DraftList: View {
var width: CGFloat = 275
var height: CGFloat = 200
#ObservedObject var api = API()
var body: some View {
VStack {
ForEach(api.draft) {item in
Spacer()
Text(item.title)
.font(.system(size: 24, weight: .bold))
.padding(.horizontal, 20)
.frame(width: 275, alignment: .leading)
.foregroundColor(Color("TextColor"))
...
}
}
.padding(.horizontal, 20)
.frame(width: width, height: height)
.background(Color("TileOrangeColor"))
.cornerRadius(15)
.shadow(color: Color.black.opacity(0.2), radius: 5, x: 0, y: 5)
}
}