Swiftui widget ios 14 从网上下载的图片问题 [英] Swiftui widget ios 14 problems with image downloaded from the internet

查看:55
本文介绍了Swiftui widget ios 14 从网上下载的图片问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我必须将来自互联网的背景图片放入小部件中,但这给我带来了以下问题.

你能告诉我哪里错了吗?

Note.swift(模型)

导入基础struct 注意:可识别,可编码 {让标题:字符串让消息:字符串var id = UUID()}

SmallWidget.swift

<预><代码>导入 SwiftUIstruct SmallWidget:查看{var 条目:注意@Environment(\.colorScheme) var colorSchemevar主体:一些视图{VStack(对齐:.center){文本(条目.标题).font(.title).胆大().minimumScaleFactor(0.5).foregroundColor(.white).阴影(颜色: Color.black,半径:1.0,x: CGFloat(4),y: CGFloat(4))文本(条目.消息).foregroundColor(Color.gray).阴影(颜色: Color.black,半径:1.0,x: CGFloat(4),y: CGFloat(4))}.frame(maxWidth: .infinity, maxHeight: .infinity).edgesIgnoringSafeArea(.all).background(NetworkImage(url: URL(string: "https://a.wattpad.com/useravatar/climaxmite.256.718018.jpg")))}}struct SmallWidget_Previews: PreviewProvider {静态 var 预览:一些视图 {let note = Note(title: "Title", message: "Mex")团体 {SmallWidget(条目:注释)SmallWidget(条目:注释).preferredColorScheme(.dark)}}}

note.swift

导入 WidgetKit导入 SwiftUI结构提供者:时间线提供者{func 占位符(在上下文中:上下文)->简单条目{SimpleEntry(注意:注意(标题:标题",消息:占位符"))}func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> ()) {let entry = SimpleEntry(note: Note(title: "Title", message: "getSnapshot"))完成(进入)}func getTimeline(in context: Context, completion: @escaping (Timeline) -> ()) {var 条目:[SimpleEntry] = []//从当前日期开始,每小时生成一个由五个条目组成的时间线.让 currentDate = Date()对于 0 中的 hourOffset ..<5 {让 entryDate = Calendar.current.date(byAdding: .hour, value: hourOffset, to: currentDate)!let entry = SimpleEntry(note: Note(title: "Title", message: "getTimeline"))条目.附加(条目)}让时间线 = 时间线(条目:条目,策略:.atEnd)完成(时间表)}}struct SimpleEntry: TimelineEntry {公众让注:注意公开出租日期:Date = Date()}struct noteEntryView : 查看 {/* 变量条目:Provider.Entryvar主体:一些视图{文本(条目.日期,样式:.time)}*/var 条目:Provider.Entry@Environment(\.widgetFamily) 私有变量 widgetFamilyvar主体:一些视图{切换widgetFamily {案例.systemSmall:SmallWidget(条目:entry.note)默认:SmallWidget(条目:entry.note)}}}@主要的结构注释:小部件{let kind: String = "note";var body: 一些 WidgetConfiguration {StaticConfiguration(kind: kind, provider: Provider()) { entry innoteEntryView(条目:条目)}.configurationDisplayName(我的小部件").description(这是一个示例小部件.").supportedFamilies([.systemSmall, .systemMedium])}}struct note_Previews:PreviewProvider {静态 var 预览:一些视图 {noteEntryView(entry: SimpleEntry(note: Note(title: "Title", message: "Mex"))).previewContext(WidgetPreviewContext(family: .systemSmall))}}

NetworkImage.swift

<预><代码>进口基金会进口结合导入 SwiftUI扩展网络图像{类视图模型:ObservableObject {@Published var imageData:数据?@Published var isLoading = falseprivate var cancellables = Set()func loadImage(来自网址:网址?){isLoading = 真守卫让 url = url else {isLoading = 假返回}URLSession.shared.dataTaskPublisher(for: url).map { $0.data }.replaceError(with: nil).receive(on: DispatchQueue.main).sink { [弱自我] inself?.imageData = $0self?.isLoading = false}.store(in: &cancellables)}}}//从 URL 下载图片结构网络图像:查看{@StateObject 私有 var viewModel = ViewModel()让网址:网址?var主体:一些视图{团体 {如果让数据 = viewModel.imageData,让 uiImage = UIImage(data: data) {图像(uiImage:uiImage).resizable().aspectRatio(contentMode: .fit)} else if viewModel.isLoading {进度视图()} 别的 {图像(系统名称:照片").resizable().aspectRatio(contentMode: .fit).redacted(原因:/*@START_MENU_TOKEN@*/.placeholder/*@END_MENU_TOKEN@*/)}}.onAppear {viewModel.loadImage(来自:url)}}}

解决方案

不幸的是,由于小部件是静态的,因此没有异步图像加载器可以在小部件中工作.它们是声明性的,但不是动态的.它们不能随着时间的推移而改变.

您可以在调用完成处理程序之前在 getSnapshot()getTimeline() 中下载图像,然后将图像与数据一起传递到条目中.

这是一些伪代码:

struct SimpleEntry: TimelineEntry {公众让注:注意公开出租日期:Date = Date()}func 占位符(在上下文中:上下文)->简单条目{//一些占位符注释 + 图像SimpleEntry(note: note, date: Date())}func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> ()) {//获取笔记和图片...note.image = 图像let entry = SimpleEntry(note: note, date: Date())完成(进入)}func getTimeline(in context: Context, completion: @escaping (Timeline) -> ()) {var 条目:[SimpleEntry] = []//获取笔记和图片...note.image = 图像let entry = SimpleEntry(note: note, date: Date())条目.附加(条目)让时间线 = ...完成(时间表)}struct noteEntryView : 查看 {var 条目:Provider.Entry@Environment(\.widgetFamily) 私有变量 widgetFamily@ViewBuildervar主体:一些视图{切换widgetFamily {案例.systemSmall:SmallWidget(注:入口.注)默认:SmallWidget(注:入口.注)}}}struct SmallWidget:查看{让我们注意:注意var主体:一些视图{图像(uiImage:note.image)...}}

I have to put a background image from the internet in a widget, but it gives me the following problem.

You can tell me where I'm wrong?

Note.swift(model)

import Foundation

struct Note: Identifiable, Codable {
    let title: String
    let message: String

    var id = UUID()
}

SmallWidget.swift


import SwiftUI

struct SmallWidget: View {
    var entry: Note
    @Environment(\.colorScheme) var colorScheme
    
    var body: some View {
        
        VStack(alignment: .center){
            Text(entry.title)
                .font(.title)
                .bold()
                .minimumScaleFactor(0.5)
                .foregroundColor(.white)
                .shadow(
                    color: Color.black,
                    radius: 1.0,
                    x: CGFloat(4),
                    y: CGFloat(4))
            Text(entry.message)
                .foregroundColor(Color.gray)
                .shadow(
                    color: Color.black,
                    radius: 1.0,
                    x: CGFloat(4),
                    y: CGFloat(4))

        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .edgesIgnoringSafeArea(.all)
        .background(NetworkImage(url: URL(string: "https://a.wattpad.com/useravatar/climaxmite.256.718018.jpg")))
    }
}

struct SmallWidget_Previews: PreviewProvider {
    static var previews: some View {
        let note = Note(title: "Title", message: "Mex")
        
        Group {
            SmallWidget(entry: note)
            SmallWidget(entry: note)
                .preferredColorScheme(.dark)
        }
    }
}

note.swift

import WidgetKit
import SwiftUI

struct Provider: TimelineProvider {
    func placeholder(in context: Context) -> SimpleEntry {
        SimpleEntry(note: Note(title: "Title", message: "placeholder"))
    }

    func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> ()) {
        let entry = SimpleEntry(note: Note(title: "Title", message: "getSnapshot"))
        completion(entry)
    }

    func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
        var entries: [SimpleEntry] = []

        // Generate a timeline consisting of five entries an hour apart, starting from the current date.
        let currentDate = Date()
        for hourOffset in 0 ..< 5 {
            let entryDate = Calendar.current.date(byAdding: .hour, value: hourOffset, to: currentDate)!
            let entry = SimpleEntry(note: Note(title: "Title", message: "getTimeline"))
            entries.append(entry)
        }

        let timeline = Timeline(entries: entries, policy: .atEnd)
        completion(timeline)
    }
}

struct SimpleEntry: TimelineEntry {
    public let note: Note
    public let date: Date = Date()
}

struct noteEntryView : View {
    /*var entry: Provider.Entry

    var body: some View {
        Text(entry.date, style: .time)
    }*/
    
    var entry: Provider.Entry
    @Environment(\.widgetFamily) private var widgetFamily
    
    var body: some View {
        switch widgetFamily {
        case .systemSmall:
            SmallWidget(entry: entry.note)
        default:
            SmallWidget(entry: entry.note)
        }
    }
}

@main
struct note: Widget {
    let kind: String = "note"

    var body: some WidgetConfiguration {
        StaticConfiguration(kind: kind, provider: Provider()) { entry in
            noteEntryView(entry: entry)
        }
        .configurationDisplayName("My Widget")
        .description("This is an example widget.")
        .supportedFamilies([.systemSmall, .systemMedium])
    }
}

struct note_Previews: PreviewProvider {
    static var previews: some View {
        noteEntryView(entry: SimpleEntry(note: Note(title: "Title", message: "Mex")))
            .previewContext(WidgetPreviewContext(family: .systemSmall))
    }
}

NetworkImage.swift



import Foundation
import Combine
import SwiftUI

extension NetworkImage {
    class ViewModel: ObservableObject {
        @Published var imageData: Data?
        @Published var isLoading = false

        private var cancellables = Set<AnyCancellable>()

        func loadImage(from url: URL?) {
            isLoading = true
            guard let url = url else {
                isLoading = false
                return
            }
            URLSession.shared.dataTaskPublisher(for: url)
                .map { $0.data }
                .replaceError(with: nil)
                .receive(on: DispatchQueue.main)
                .sink { [weak self] in
                    self?.imageData = $0
                    self?.isLoading = false
                }
                .store(in: &cancellables)
        }
    }
}

// Download image from URL
struct NetworkImage: View {
    @StateObject private var viewModel = ViewModel()

    let url: URL?

    var body: some View {
        Group {
            if let data = viewModel.imageData, let uiImage = UIImage(data: data) {
                Image(uiImage: uiImage)
                    .resizable()
                    .aspectRatio(contentMode: .fit)
            } else if viewModel.isLoading {
                ProgressView()
            } else {
                Image(systemName: "photo")
                    .resizable()
                    .aspectRatio(contentMode: .fit)
                    .redacted(reason: /*@START_MENU_TOKEN@*/.placeholder/*@END_MENU_TOKEN@*/)
            }
        }
        .onAppear {
            viewModel.loadImage(from: url)
        }
    }
}

解决方案

Unfortunately, no async image loader is going to work in a widget since widgets are static. They're declarative, but not dynamic. They're not allowed to change over time.

You can download the image in getSnapshot() and getTimeline() before calling the completion handler, then pass the image along with data in the entry.

Here's some pseudo code:

struct SimpleEntry: TimelineEntry {
    public let note: Note
    public let date: Date = Date()
}

func placeholder(in context: Context) -> SimpleEntry {
    // Some placeholder note + image
    SimpleEntry(note: note, date: Date())
}

func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> ()) {
  // fetch note and image
  ...

  note.image = image

  let entry = SimpleEntry(note: note, date: Date())
  completion(entry)
}

func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
  var entries: [SimpleEntry] = []
  
  // fetch note and image
  ...

  note.image = image

  let entry = SimpleEntry(note: note, date: Date())
  entries.append(entry)

  let timeline = ...
  completion(timeline)
}

struct noteEntryView : View {
    var entry: Provider.Entry

    @Environment(\.widgetFamily) private var widgetFamily
    
    @ViewBuilder
    var body: some View {
        switch widgetFamily {
        case .systemSmall:
            SmallWidget(note: entry.note)
        default:
            SmallWidget(note: entry.note)
        }
    }
}

struct SmallWidget: View {
    let note: Note

    var body: some View {
      Image(uiImage: note.image)
      ...
    }
}

这篇关于Swiftui widget ios 14 从网上下载的图片问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆