簡體   English   中英

SwiftUI iOS 小部件 – 一段時間后的圖像被裁剪

[英]SwiftUI iOS Widget – Image after some time gets cropped

我正在使用 SwiftUI 創建 iOS 14+ Widget,我面臨着非常奇怪的情況。 問題是我的小部件中只有一個“完整小部件”圖像(也許值得注意,它是從 inte.net 下載的,但在顯示它已經加載並注入為UIImage )像那樣正確顯示:

在此處輸入圖像描述

但過了一段時間,在更多 iPhone 解鎖 BOOM 之后,我得到了這個:

在此處輸入圖像描述

一些觀察:

  • 它也發生在systemLarge系列中 - 圖像寬度的相同百分比被裁剪
  • 圖像總是按完全相同的寬度百分比水平裁剪
  • 當我的牆紙不是純黑色時,裁剪部分也會填充黑色
  • 無論我有什么用戶界面風格(淺色/深色),裁剪部分都充滿黑色

為了方便起見,我創建了導致錯誤的最小示例:

所以,我有這個EntryView

struct EntryView: View {

    var viewModel: EntryViewModel

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

用作Widget本身的內容

struct SomeWidget: Widget {

    var body: some WidgetConfiguration {
        StaticConfiguration(
            kind: xxx,
            provider: SomeWidgetTimelineProvider(xxx)
        ) { item in
            view(for: item)
        }
            .configurationDisplayName(xxx)
            .description(xxx)
            .supportedFamilies([.systemMedium, .systemLarge])
    }

    private func view(for item: Item) -> some View {
        Group {
            switch item {
            case .aaa(let xxx):
                EntryView(viewModel: xxx)
            case .bbb(let xxx):
                EntryView(viewModel: xxx)
            }
        }
    }
}

並且這個Widget被包裹在WidgetBundle

@main
struct SomeWidgets: WidgetBundle {

    var body: some Widget {
        SomeWidget()
        SomeWidget()
    }
}

也許我還應該展示負責下載圖像的邏輯。 我只使用在后台隊列上調用的簡單Data(contentsOf:)同步方法,然后在主隊列上調用TimelineProvider的回調:

final class SomeWidgetTimelineProvider: TimelineProvider {

    ...

    func getTimeline(in context: Context, completion: @escaping (Timeline<SomeEntry>) -> Void) {
        getSomeModel { model in
            prefetchImage(for: model) { result in
                switch result {
                case .success(let image):
                    completion(
                        Timeline(
                            entries: [.aaa(EntryViewModel(image: image)],
                            policy: .after(Date() + 30 * 60)
                        )
                    )
                case .error(let error):
                    completion(
                        Timeline(
                            entries: [.bbb(xxx)],
                            policy: .after(Date() + 30 * 60)
                        )
                    )
                }
            }
        }
    }

    private func prefetchImage(for model: SomeModel, completion: @escaping (Result<UIImage, Error>) -> Void) {
        DispatchQueue.global(qos: .background).async {
            guard
                let imageURL = URL(string: model.imageUrl),
                let data = try? Data(contentsOf: imageURL)
            else {
                DispatchQueue.main.async {
                    completion(.failure(xxx))
                }
            }
            let image = UIImage(data: data)
            DispatchQueue.main.async {
                completion(.success(image))
            }
        }
    }
}

所以我的問題是,我的布局或獲取邏輯有問題嗎? 我錯過了什么?

感謝您的任何幫助!

有幾個問題是可能的。

1.分辨率問題

問題通常源於您的小部件的圖像分辨率。 以像素為單位的圖像分辨率必須與以點為單位的 Widget 分辨率相關。 在下面的 pivot 表中,您可以看到某些 iPhone 型號使用的分辨率(以點 (pts) 為單位)。 點與像素不同,因為它們會根據 PPI 改變大小。 當分辨率為163 PPI時,1 點等於 1 個像素。 Retina 時代之前的所有 iPhone 都是如此。 然而,今天,在460 PPI的情況下,iPhone 12 Pro 的 1 點等於 3 像素寬和 3 像素向下,或總共 9 像素。

因此,對於 iPhone 13 Pro Max,Medium Widget 的圖像尺寸不應超過 1092 x 510 像素(72 像素/英寸)。 為 iOS 小部件使用 4K 源圖像是非常不合適的。


Model 屏幕尺寸(分) 小部件 (pts) 中等小部件(pts) 大部件 (pts)
iPhone 13 專業版 428 x 926 170×170 364 x 170 364 x 382
iPhone 11 414 x 896 169×169 360 x 169 360 x 379
iPhone 8 加 414×736 159 x 159 348 x 157 348 x 357
iPhone 12 專業版 390 x 844 158×158 338 x 158 338 x 354
iPhone X 375 x 812 155×155 329 x 155 329 x 345
iPhone 7 375 x 667 148×148 321×148 321×324
iPhone SE 320 x 568 141×141 292 x 141 292×311

2.文件格式問題

小部件圖像必須為 8 位.png格式。 圖像可能包含 Alpha 通道,但不應包含任何透明區域(即 Alpha 通道應為 100% 白色)。

3.調度QoS.QoSClass

使用優先級最高的服務質量,即.userInteractive案例:

DispatchQueue.global(qos: .userInteractive).async { ... }

4.無效文件問題

嘗試使用不同的.png 您從 Internet 下載的圖像很可能已損壞或損壞。 圖像可能由於傳輸中斷、惡意軟件/病毒感染、SSD 壞塊等而損壞。

5.清理

有時,如果您觀察到不正常的功能、索引或緩慢,您必須刪除/清理構建。 如果是這樣,請從~/Library/Developer/文件夾中刪除您的項目構建。 接下來,在導航面板中的 Xcode、select 項目圖標中,然后按Command - Shift - K快捷方式來清理構建。

然后,關閉Xcode、go到~/Library/Developer/Xcode/DerivedData/ModuleCache目錄並刪除緩存。

6. Xcode版本

在 Xcode 14 的發布版本上運行您的項目。不要使用測試版。

只是讓任何人知道,安迪的回答有好處。 無論如何,對我來說,iOS 16 上似乎不再存在問題。所以這可能是操作系統本身的一個錯誤,因為沒有任何代碼更改,相同的代碼適用於 iOS 16 而不是 Z1BDF605991920DB11CBDF8508204

我遇到了一個非常相似的問題,我想在這里發布我的解決方案以防有人遇到相同的問題,因為他們可能會像我一樣在這個線程上結束。

我的小部件具有添加自定義圖像作為小部件背景的功能,如果圖像太大,小部件就會完全變成灰色,並帶有經過編輯的占位符(我假設是因為時間線提供商無法提供這些大圖像)。

所以解決方案是以編程方式將圖像裁剪為一些預定義的尺寸。 我發現對於幾乎所有設備,寬度或高度的最大尺寸1024 像素都完全可以,並且適用於每個 iOS 版本(iOS 16 是撰寫本文時的最新版本)。 僅對於iPhone 11 和 XR (具有相同的屏幕), 824 像素的高度/寬度是我發現的第一個有效值。

我找不到關於這些尺寸的任何文檔,它們可能會在未來的 iOS 版本中發生變化。

如果您使用存儲在項目資源中的 static 圖像,則將824圖像用於2x比例,將1024圖像用於3x比例。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM