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 entry struct for every cell? Or is there a way to put array/list in the entry struct and append 4 data to that entry from I got from the request? Does TimelineEntry have subscripts I can use? Like, entry[0].title
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)
}