【Swift】画像 生 Data は UIImage や NSImage で変更されることを知る

例えば、WEB上の下のような

「imgur で削除された」ことを表す PNG 画像 のデータは、

以下のようにして Data を取得できますよね。


let data = try! Data(
  contentsOf: URL(string: "https://i.imgur.com/removed.png")!
)

それを一度、UIKit UIImage に適用した、としても、

それは再び同じ Data 型 として同じものが抽出できるイメージでいました。


👉 UIImage | Apple Developer Documentation
👉 Data | Apple Developer Documentation

しかし、同じものが抽出できません。

適用前と後で抽出したものが違うようです。


let data = try! Data(
  contentsOf: URL(string: "https://i.imgur.com/removed.png")!
)
let uiImage = UIImage(data: data)!
let data2 = uiImage.pngData()!

print(data == data2) // false
print(data, data2)   // 503 bytes 1134 bytes

なんでや、何が違うんや。

 

🤔 Base64Encoding

テキスト化して見てみます。

違いの量は分かります。


let data = try! Data(
  contentsOf: URL(string: "https://i.imgur.com/removed.png")!
)
let uiImage = UIImage(data: data)!
let data2 = uiImage.pngData()!

print(
  data,
  data.base64EncodedString()
)
print(
  data2,
  data2.base64EncodedString()
)

// 503 bytes 
// iVBORw0KGgoAAAANSUhEUgAAAKEAAABRAQMAAACADVTsAAAABlBMVEUiIiL///9ehyAxAAABrElEQVR4Xu3QL2/bQBgG8NdRlrnMNqxu1eVAahCQVAEF03STbsuBSFVZYEBBoJ2RjZ0Hljuy6IZaUlUlpfsKRUmZP4JTNJixkEm7nJu/Mxlot0l7JJOfXj06P/D3xvkBQH/lqoEC7WVvzqM0k/f4+Gat2nt7ppqeCjCbiJX6HmN7vnca4LLc0BljH/yZ0ZejDQXGlA9GmYSthoumVw1wZ6PByxjrpxmeZq0hbMcDXPCHGVB4hHCAkgUKrrNSulawelPRCH37mu4fR1EdZYPwnTA6UZoQfteoMSmPCFVcgYmUmmCuPMKkIAtNFjqS+hWyOo+MzmVsb12NS1aFazThe1Ztr2qYBklWvcPKCKG+TA/MGwjqDcI4n1Pko+1E5KM9TRz75fGB0qWv1Vlq/Bo9Gzqo3oqu7g991G1bVQmp8IQcdeRtEGpyxoVVB5eNLob0qS6xpaJc5+J7Wx+wkwct5SoSn2vCOORKrHZk0lC69tAbm4a2g0grEuknvd9tb61XhqK8hz+d/xG/cft5fD0dvxA7qsLrj+EXWqBugRbeHl6qcbCr4Ba+7Tn88/kJk4CIztd1IrIAAAAASUVORK5CYII=

// 1134 bytes 
// iVBORw0KGgoAAAANSUhEUgAAAKEAAABRCAMAAACNHTadAAAABlBMVEUiIiL///9ehyAxAAAAhGVYSWZNTQAqAAAACAAFARIAAwAAAAEAAQAAARoABQAAAAEAAABKARsABQAAAAEAAABSASgAAwAAAAEAAgAAh2kABAAAAAEAAABaAAAAAAAAAEgAAAABAAAASAAAAAEAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAoaADAAQAAAABAAAAUQAAAACmJMYkAAAACXBIWXMAAAsTAAALEwEAmpwYAAADfklEQVRoBe1Xi3bqMAyD///pa1mS4zTAxh6s200Oi21ZftQtYb1c9toT2BPYE3jlBK5cl+tSdEUWyquAbOVE/azXrQ6vbDJmSgrE9QKLiBxhJME80kAqTSWMZIaMWWu/D8ng7CR7Ql9Y2BMNhR+JuZWkiV2i4pVC6QB/ZKEG+2nyFtIaLT54arK71ce4siQJfVrMHca9SaDXpZ6O6qbxChuXhSbISGeRn24uA7KhVkVZOhK6zI6SeG+Gh4CP9eYSIXtl6AsSoJ7V+Tkk0fFOw3gF2JlpP7JlQ5U67yXSGCmpYR2+y+pQ32Xd2mwj+otYhNuZ8Cu2LDoKHczh+BltfeZP1uDPjGVXfdkE4nm7/8jd9yz93aA2qKlL5FvAw9iHzjnzY+oNLw+tPIVxbvkIA9NHY0psCYKUzhqpPXawiqODRgfzDRioMjIhbFEBeA1e8pvJaGdBskQoscOMJRUWVIqS5DUU6uDMI2E4CbW3WGcV1D1M2RCrvdbSIYuYKgkRq6xQpBtO/9jSG8M1awQABKrRDwcywpHOUoUcokQzPaSjlN2eUQUl+ypGgNKbMDMgfg4c+BtdnBY1/J0nNEWLaapT3IgXa4iWOcBuwZwQOBsn9QU5Rs0x8jIz94R0O/Hww/YN0HeBXvvcE6lIMtNHLlXLjOIlG3pGufvWg0qScntX2tvOGX2COgd+znpnWc3tc7V29J7Ar53AW1+Ut/yPLhyxI96a5aPIZ3yfzTfirVne70Lncf8BqGPT/zj5FNYRS/9w+phHER/kTpshk1G/wE4TlKp4o09cQvyZApUIuEAPyPAPpzRGIrxIzMXdiW2FtKqAjFy2RoJPYaI1J9RY8svRzIaQ6fpr4JLGsQ6cpbzKw6vyQGm5VMYFz/laJFAs1ibRZoJp2E1mWSN27syWiBBYkk2ECmsgtiSbOCSgJ/ZGWSw44b+/WjRII4OsFVFE5iV9eg5ZCg4Qi0WmsmXAhCSPocvuu0EHshKBndaKTLXRSYuCiiWUlWmMbEDrKEiyfRn7DZvb+obUX5CSz/0XJNop9gT2BPYE9gT2BPYE9gT2BPYE/sYEzv0P/O+Ycb5k4V1Mil7c/MKnN0F7SXvtlWVj+d5ZvUUD1BOWHrz6nK1D93neDj3bM3SIhw+3Ervvfj2BP3aX+zOlrjp0Nj1HeLamdj97AnsCewL/+QT+AeEGA83c9VTUAAAAAElFTkSuQmCC

全然違いますね。

「PNGフォーマット」を表す先頭部分は同じなことも分かります。

さらに、

Xcode ウインドウからデスクトップ上や好きな位置に、

コピペで画像ファイルを簡単に作成できるので便利です。


# コピーして
❯ pbpaste | base64 --decode > 503.png

# さらにコピーして
❯ pbpaste | base64 --decode > 1134.png

❯ ls -al
-rw-r--r--@  1 chanzmao  staff    503 Oct 11 10:50 503.png
-rw-r--r--@  1 chanzmao  staff   1134 Oct 11 10:51 1134.png

これで2つの処理のタイミングでの Data を利用した画像ファイルが作成できました。

また、xxd でも16進数を使って似たようなことができそうです。


❯ xxd 503.png | head
00000000: 8950 4e47 0d0a 1a0a 0000 000d 4948 4452  .PNG........IHDR
00000010: 0000 00a1 0000 0051 0103 0000 0080 0d54  .......Q.......T
00000020: ec00 0000 0650 4c54 4522 2222 ffff ff5e  .....PLTE"""...^
00000030: 8720 3100 0001 ac49 4441 5478 5eed d02f  . 1....IDATx^../
00000040: 6fdb 4018 06f0 d751 96b9 cc36 ac6e d5e5  o.@....Q...6.n..
00000050: 406a 1090 5401 05d3 7493 6ecb 8148 5559  @j..T...t.n..HUY
00000060: 6040 41a0 9d91 8d9d 0796 3bb2 e886 5a52  `@A.......;...ZR
00000070: 5525 a5fb 0a45 4999 3f82 5334 98b1 9049  U%...EI.?.S4...I
00000080: bb9c 9bbf 3319 68b7 497b 2493 9f5e 3d3a  ....3.h.I{$..^=:
00000090: 3ff0 f7c6 f901 407f e5aa 8102 ed65 6fce  ?.....@......eo.


❯ xxd original.png > original.hex
❯ xxd -r original.hex > revert.png

👉️ xxdコマンドで画像データを16進数変換してiOSライブラリ内に画像データを内包する

 

🤔 file コマンド

先に作成した2つのファイルを file コマンドで見てみます。


❯ file 503.png 1134.png
503.png:  PNG image data, 161 x 81, 1-bit colormap, non-interlaced
1134.png: PNG image data, 161 x 81, 8-bit colormap, non-interlaced

少し違うことが分かります。


1-bit colormap
8-bit colormap

PNGファイルのプロファイル的な何かでしょうか。

 

🤔 sips コマンド

macOS 標準のコマンドらしいです。

2つの実行結果を diff に入力します。


❯ diff --side-by-side <(sips -g all 503.png) <(sips -g all 1134.png)
~/Desktop/503.png            | ~/Desktop/1134.png
  pixelWidth: 161                pixelWidth: 161
  pixelHeight: 81                pixelHeight: 81
  typeIdentifier: public.png     typeIdentifier: public.png
  format: png                    format: png
  formatOptions: default         formatOptions: default
  dpiWidth: 72.000               dpiWidth: 72.000
  dpiHeight: 72.000              dpiHeight: 72.000
  samplesPerPixel: 3             samplesPerPixel: 3
  bitsPerSample: 1           |   bitsPerSample: 8
  hasAlpha: no                   hasAlpha: no
  space: RGB                     space: RGB

以下の部分が違います。


bitsPerSample: 1	           |	   bitsPerSample: 8

( 現在、記事更新中 ... )


【SwiftUI】UIImage / NSImage の Image への抽象化

どちらが好きですか、以下2つのコード。

 

■ 1つ目


import SwiftUI

public extension Image {
    #if canImport(AppKit)
    init(image: NSImage) {
        self = Image(nsImage: image)
    }
    #endif

    #if canImport(UIKit)
    init(image: UIImage) {
        self = Image(uiImage: image)
    }
    #endif
}

特徴:
- 拡張機能 (extension) を使って、Imageに新しいinitイニシャライザを追加しています。
- プラットフォームごとに異なる型 (NSImageやUIImage) を直接引数に取ります。
- プラットフォーム依存の条件付きで、NSImage(macOS)またはUIImage(iOS)を使用してImageを初期化しています。

メリット:
- 各プラットフォームに対応したイニシャライザが個別に用意されており、Imageの初期化が直感的です。

デメリット
- プラットフォームごとにinitメソッドが別々に定義されているため、共通の型を扱うのが難しい。

 

■ 2つ目


#if canImport(AppKit)
import AppKit
public typealias PlatformImage = NSImage
#elseif canImport(UIKit)
import UIKit
public typealias PlatformImage = UIImage
#endif

import SwiftUI

extension Image {
    init(platformImage: PlatformImage) {
        #if canImport(UIKit)
        self = Image(uiImage: platformImage)
        #elseif canImport(AppKit)
        self = Image(nsImage: platformImage)
        #endif
    }
}

特徴:
- PlatformImageという型エイリアスを使って、macOSのNSImageとiOSのUIImageを抽象化しています。
- platformImageという共通の引数型を持つイニシャライザを追加しています。これにより、プラットフォームごとにImageを初期化しますが、型エイリアスによって共通化されています。

メリット:
- 抽象化されているため、呼び出し側のコードがプラットフォームに依存しません。つまり、共通のコードでPlatformImage型を使えば、iOSでもmacOSでも同じコードで動作します。
- 可読性が高く、メンテナンスが容易です。プラットフォームごとにメソッドを分ける必要がなく、1つのメソッドで対応しています。

デメリット:
- プラットフォームごとに異なる処理を追加する際に、多少複雑になる可能性があります。

 

■ まとめ

AIによると、

結論:どちらが良いか?

2つ目のコードの方が一般的に推奨されます。理由は、コードの抽象化によって、呼び出し側がプラットフォームに依存しない形でImageを扱うことができるためです。メンテナンス性が高く、同じコードベースで複数のプラットフォームをサポートしやすくなります。

ただし、プラットフォームごとに異なる処理が必要なケースでは、1つ目のコードの方が直感的に分かりやすい場合もあるので、状況に応じて選択が変わることがあります。

ということです。

私的には、どっちも勉強になります、としか。



【SwiftUI】Create Draggable Reorder ListView without List

👉 Drag and Drop List In SwiftUI. In this article, We will explore how to… | by Mobile Apps Academy | Medium

よくある UI の挙動を SwiftUI でどれだけシンプルに作れるのか。

やってみました。

本来は、何かを NSItemProvider() 経由で、

ドロップ先に渡すのが役目っぽいけども、

DropDelegate の便利さを利用して

配列を並び替えるイメージ。

並び替えのアニメーションは withAnimation デフォルトに頼る。

iOS と macOS、Preview と シュミレータ と 実機、OS バージョンなど、

互換しようとするといろいろありそう。

ここらのコンポーネントはまだ不安定な感じ ?