I am learning how to create a widget for the first time. I have finished the design and everything is good to go and I am ready to put it on my app. However, I found a problem that still needs to be addressed. I am making a countdown that updates every second. While updating, it writes brand new images every second and builds up the memory, while the disk shows 20-30mb per second and the CPU is stuck at 90-95%. How can I simply update the label without updating the image?
Here my main code:
struct Provider: IntentTimelineProvider {
func placeholder(in context: Context) -> SimpleEntry {
SimpleEntry(date: Date(), configuration: ConfigurationIntent())
}
func getSnapshot(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (SimpleEntry) -> ()) {
let entry = SimpleEntry(date: Date(), configuration: configuration)
completion(entry)
}
func getTimeline(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
var entries: [SimpleEntry] = []
let currentDate = Date()
let entryDate = Calendar.current.date(byAdding: .second, value: 1, to: currentDate)!
let entry = SimpleEntry(date: entryDate, configuration: configuration)
entries.append(entry)
let timeline = Timeline(entries: entries, policy: .atEnd)
let _ = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { (timerSec) in
WidgetCenter.shared.reloadAllTimelines()
}
completion(timeline)
}
}
struct SimpleEntry: TimelineEntry {
let date: Date
let configuration: ConfigurationIntent
}
struct Widget_Practice_WidgetEntryView : View {
@Environment(\.widgetFamily) var family
var entry: Provider.Entry
var body: some View {
switch family {
case .systemSmall: CreateSmallDesign()
case .systemMedium: CreateMediumDesign()
case .systemLarge: CreateLargeDesign()
case .systemExtraLarge: CreateExLargeDesign()
@unknown default: CreateSmallDesign()
}
}
}
@main
struct Widget_Practice_Widget: Widget {
let kind: String = "Widget_Practice_Widget"
var body: some WidgetConfiguration {
IntentConfiguration(kind: kind, intent: ConfigurationIntent.self, provider: Provider()) { entry in
Widget_Practice_WidgetEntryView(entry: entry)
}
.configurationDisplayName("Edit Widget")
.description("This is an example widget.")
}
}
struct Widget_Practice_Widget_Previews: PreviewProvider {
static var previews: some View {
Widget_Practice_WidgetEntryView(entry: SimpleEntry(date: Date(), configuration: ConfigurationIntent()))
.previewContext(WidgetPreviewContext(family: .systemSmall))
}
}
Here my code for medium size view:
struct CreateMediumDesign : View {
var body: some View {
ZStack {
Image(uiImage: resizeImage(image: UIImage(named: demoCountdownDatabase[1].imageName), targetSize: CGSize(width: 250, height: 250))!)
.resizable()
.scaledToFill()
.blur(radius: 30)
VStack {
HStack(alignment: .center, spacing: 15) {
Text("")
Image(uiImage: resizeImage(image: UIImage(named: demoCountdownDatabase[1].imageName), targetSize: CGSize(width: 250, height: 250))!)
.resizable()
.scaledToFill()
.frame(width: 80, height: 80, alignment: .center)
.clipShape(RoundedRectangle(cornerRadius: 15))
.overlay(RoundedRectangle(cornerRadius: 15).stroke(Color.white, lineWidth: 2))
.shadow(radius: 10)
VStack(alignment: .leading) {
Text(demoCountdownDatabase[1].title)
.font(.largeTitle)
.foregroundColor(.white)
.shadow(color: .black, radius: 2, x: 0, y: 0)
.scaledToFill()
Text(demoCountdownDatabase[1].subtitle)
.font(.body)
.foregroundColor(.white)
.shadow(color: .black, radius: 2, x: 0, y: 0)
.scaledToFill()
}
Spacer()
}
GeometryReader { geometry in
HStack(alignment: .center, spacing: 0) {
Text(fullDateUntilString(date: demoCountdownDatabase[1].toDate, title: "Year"))
.font(.footnote)
.frame(width: geometry.size.width / 6.1, height: 35)
.foregroundColor(.white)
.shadow(color: .black, radius: 2, x: 0, y: 0)
.multilineTextAlignment(.center)
Text(fullDateUntilString(date: demoCountdownDatabase[1].toDate, title: "Month"))
.font(.footnote)
.frame(width: geometry.size.width / 6.1, height: 35)
.foregroundColor(.white)
.shadow(color: .black, radius: 2, x: 0, y: 0)
.multilineTextAlignment(.center)
Text(fullDateUntilString(date: demoCountdownDatabase[1].toDate, title: "Day"))
.font(.footnote)
.frame(width: geometry.size.width / 6.1, height: 35)
.foregroundColor(.white)
.shadow(color: .black, radius: 2, x: 0, y: 0)
.multilineTextAlignment(.center)
Text(fullDateUntilString(date: demoCountdownDatabase[1].toDate, title: "Hour"))
.font(.footnote)
.frame(width: geometry.size.width / 6.1, height: 35)
.foregroundColor(.white)
.shadow(color: .black, radius: 2, x: 0, y: 0)
.multilineTextAlignment(.center)
Text(fullDateUntilString(date: demoCountdownDatabase[1].toDate, title: "Minute"))
.font(.footnote)
.frame(width: geometry.size.width / 6.1, height: 35)
.foregroundColor(.white)
.shadow(color: .black, radius: 2, x: 0, y: 0)
.multilineTextAlignment(.center)
Text(fullDateUntilString(date: demoCountdownDatabase[1].toDate, title: "Second"))
.font(.footnote)
.frame(width: geometry.size.width / 6.1, height: 35)
.foregroundColor(.white)
.shadow(color: .black, radius: 2, x: 0, y: 0)
.multilineTextAlignment(.center)
}
}
.frame(height: 15)
}
}
}
}
Here is my code to resize an image to reduce its size:
func resizeImage(image: UIImage?, targetSize: CGSize) -> UIImage? {
if image != nil {
let size = image!.size
let widthRatio = targetSize.width / size.width
let heightRatio = targetSize.height / size.height
var newSize: CGSize
if(widthRatio > heightRatio) {
newSize = CGSize(width: size.width * heightRatio, height: size.height * heightRatio)
} else {
newSize = CGSize(width: size.width * widthRatio, height: size.height * widthRatio)
}
let rect = CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height)
UIGraphicsBeginImageContextWithOptions(newSize, false, 1.0)
image!.draw(in: rect)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage ?? nil
} else {
return nil
}
}
UPDATE: I wanted to make it clear that I was trying to reload only the text, and not the image. Refreshing every second worked perfectly for me, however I want it to ignore the image when reloading / refreshing. Focusing on text only is necessary since reloading every second causes the image to reload every second, which causes hardware to crash.