<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>SwiftUI &#8211; 隨心所欲</title>
	<atom:link href="https://doeverythingiwant.com/tag/swiftui-2/feed/" rel="self" type="application/rss+xml" />
	<link>https://doeverythingiwant.com</link>
	<description>iOS Developer 的隨筆記錄</description>
	<lastBuildDate>Sat, 01 Feb 2025 09:26:33 +0000</lastBuildDate>
	<language>zh-TW</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>

<image>
	<url>https://doeverythingiwant.com/wp-content/uploads/2025/08/cropped-0802.png</url>
	<title>SwiftUI &#8211; 隨心所欲</title>
	<link>https://doeverythingiwant.com</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>[iOS] Xcode Preview trouble shootings</title>
		<link>https://doeverythingiwant.com/ios-xcode-preview-trouble-shootings/</link>
					<comments>https://doeverythingiwant.com/ios-xcode-preview-trouble-shootings/#respond</comments>
		
		<dc:creator><![CDATA[艾普利]]></dc:creator>
		<pubDate>Sat, 01 Feb 2025 09:26:31 +0000</pubDate>
				<category><![CDATA[寫程式]]></category>
		<category><![CDATA[Preview]]></category>
		<category><![CDATA[SwiftUI]]></category>
		<category><![CDATA[Xcode]]></category>
		<guid isPermaLink="false">https://doeverythingiwant.com/?p=2377</guid>

					<description><![CDATA[自從開始使用 SwiftUI 後，Xcode 內的 Prev&#8230;]]></description>
										<content:encoded><![CDATA[
<p>自從開始使用 SwiftUI 後，Xcode 內的 Preview 就變得非常重要，且它本身也是非常好用的東西，可以即時顯示 UI的樣式，大多數的情況下，可以說是完全反應結果，真的很好用。</p>



<p>但是在某些情況下就是無法顯示出來，會讓人覺得很煩燥，有一種解決方式是把 Perviews simulator 刪除，讓它自動重新再產生。</p>



<span id="more-2377"></span>



<p>有不少人覺得Preview 不好用，主要有幾個理由：如果不是 M系列 CPU 根本很難出現、電腦會變慢、三步五十就壞掉…等等。我自己剛開始在用的時候，也常常不知道為什麼它會跑不出來或是壞掉，簡單去研究一下才知道一些事情，也大概知道為什麼會這樣。</p>



<p>如果你的 Xcode 也常常Preview 上一秒還可以用下一秒就跑不出來的話，接下來要說的方法，說不定可以解決你的煩惱!</p>



<h2 class="wp-block-heading">認識一下 Xcode Preview</h2>



<p>在使用 SwiftUI 時，Xcode 畫面右邊這區就是 Preview畫面出現的地方，在 Xcode 裡是叫 Canvas，它是用來呈現寫在 <code>#Preview</code> 裡的程式碼，雖然不知道是不是大家都這樣，但我習慣叫它Preview 而不是 Canvas。實際上它也是把模擬器跑起來，所以若不是使用Apple 的 M 系列核心，會很難跑起來…</p>



<figure class="wp-block-image size-large"><img fetchpriority="high" decoding="async" width="1024" height="617" src="https://doeverythingiwant.com/wp-content/uploads/2025/02/Preview-1024x617.png" alt="" class="wp-image-2390" srcset="https://doeverythingiwant.com/wp-content/uploads/2025/02/Preview-1024x617.png 1024w, https://doeverythingiwant.com/wp-content/uploads/2025/02/Preview-300x181.png 300w, https://doeverythingiwant.com/wp-content/uploads/2025/02/Preview-768x463.png 768w, https://doeverythingiwant.com/wp-content/uploads/2025/02/Preview-1536x926.png 1536w, https://doeverythingiwant.com/wp-content/uploads/2025/02/Preview-2048x1234.png 2048w, https://doeverythingiwant.com/wp-content/uploads/2025/02/Preview-1920x1157.png 1920w, https://doeverythingiwant.com/wp-content/uploads/2025/02/Preview-1170x705.png 1170w, https://doeverythingiwant.com/wp-content/uploads/2025/02/Preview-585x353.png 585w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<h2 class="wp-block-heading">Preview的一些問題</h2>



<p>Preview 算是可以即時顯示出程式碼修改後的結果，是真的很方便，但是有時候也是會跑不出來，當然語法寫錯或是有些資料沒有給它(Preview 使用到得資料需要寫在 <code>#Preview</code> 裡，它是獨立的)，就無法顯示出來。這樣的問題可以檢查一下基本上就可以解決了，但有時候就是明明沒什麼問題，但一直轉半天畫面都沒有出現。</p>



<p>在個人經驗裡有二個情況比較容易發生</p>



<ul class="wp-block-list">
<li>在二、三個專案中切換</li>



<li>在不同的Git branch 中切換</li>
</ul>



<p>實作過程中難免會需要在不同的專案或不同的 Branch 中切換，就可能會<strong>遇到上一秒還顯示出現，下一秒就顯示不出來</strong>情況。</p>



<h2 class="wp-block-heading">解決方式</h2>



<p>如果是在同一個專案發生這個情況可以先試試 <strong>Clear Build + Delete DerivedData </strong>，也許就可以解決這個問題，但如果怎麼樣都無法解決的話，可以試試下面方式。</p>



<p>順便提一下，想刪除 <strong>DerivedData</strong> 可以從下圖開啟資料夾，再把資料夾裡的東西全丟到垃圾筒即可</p>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="677" src="https://doeverythingiwant.com/wp-content/uploads/2025/02/0201-1024x677.png" alt="" class="wp-image-2392" srcset="https://doeverythingiwant.com/wp-content/uploads/2025/02/0201-1024x677.png 1024w, https://doeverythingiwant.com/wp-content/uploads/2025/02/0201-300x198.png 300w, https://doeverythingiwant.com/wp-content/uploads/2025/02/0201-768x508.png 768w, https://doeverythingiwant.com/wp-content/uploads/2025/02/0201-1536x1016.png 1536w, https://doeverythingiwant.com/wp-content/uploads/2025/02/0201-1170x774.png 1170w, https://doeverythingiwant.com/wp-content/uploads/2025/02/0201-780x516.png 780w, https://doeverythingiwant.com/wp-content/uploads/2025/02/0201-585x387.png 585w, https://doeverythingiwant.com/wp-content/uploads/2025/02/0201-263x175.png 263w, https://doeverythingiwant.com/wp-content/uploads/2025/02/0201.png 1666w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="541" src="https://doeverythingiwant.com/wp-content/uploads/2025/02/02012-1024x541.png" alt="" class="wp-image-2393" srcset="https://doeverythingiwant.com/wp-content/uploads/2025/02/02012-1024x541.png 1024w, https://doeverythingiwant.com/wp-content/uploads/2025/02/02012-300x158.png 300w, https://doeverythingiwant.com/wp-content/uploads/2025/02/02012-768x406.png 768w, https://doeverythingiwant.com/wp-content/uploads/2025/02/02012-1170x618.png 1170w, https://doeverythingiwant.com/wp-content/uploads/2025/02/02012-585x309.png 585w, https://doeverythingiwant.com/wp-content/uploads/2025/02/02012.png 1522w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>剛剛有說到，其實Preview 也是跑模擬器，所以直接全部刪除讓它們重新產生，就和<strong>Delete DerivedData</strong> 的作法差不多，因為不確定到底是什麼問題就直接全刪，和「重開機治百病」一樣的感覺 <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>



<p>首先，開啟Terminal，不需要移動到其他位置，然後輸入以下指令</p>



<pre class="wp-block-code"><code>xcrun simctl --set previews delete unavailable</code></pre>



<p>會需要跑一點點時間，跑完之後會出現以下文字</p>



<pre class="wp-block-code"><code>Using Previews Device Set: '/Users/userName/Library/Developer/Xcode/UserData/Previews/Simulator Devices'</code></pre>



<p>這樣當你重新開啟 Xcode 之後，Preview 的模擬器就會再重新Create，這樣也許可以解決 Preview 轉半天沒有出現的情況。</p>



<p>希望這個方式能有幫助~</p>



<p>最後，祝大家 Coding 愉快!!</p>



<p></p>
]]></content:encoded>
					
					<wfw:commentRss>https://doeverythingiwant.com/ios-xcode-preview-trouble-shootings/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>[iOS] Network Monitor</title>
		<link>https://doeverythingiwant.com/ios-network-monitor/</link>
					<comments>https://doeverythingiwant.com/ios-network-monitor/#respond</comments>
		
		<dc:creator><![CDATA[艾普利]]></dc:creator>
		<pubDate>Sun, 20 Oct 2024 14:25:17 +0000</pubDate>
				<category><![CDATA[寫程式]]></category>
		<category><![CDATA[iOS]]></category>
		<category><![CDATA[network monitor]]></category>
		<category><![CDATA[SwiftUI]]></category>
		<guid isPermaLink="false">https://doeverythingiwant.com/?p=2361</guid>

					<description><![CDATA[Photo by Frederik Lipfert on U&#8230;]]></description>
										<content:encoded><![CDATA[
<p class="has-text-align-center">Photo by <a href="https://unsplash.com/@frederikli?utm_content=creditCopyText&amp;utm_medium=referral&amp;utm_source=unsplash" target="_blank" rel="noopener">Frederik Lipfert</a> on <a href="https://unsplash.com/photos/a-person-holding-an-iphone-in-their-hand-y67dwfB2AiM?utm_content=creditCopyText&amp;utm_medium=referral&amp;utm_source=unsplash" target="_blank" rel="noopener">Unsplash</a></p>



<p>在iOS12之前想要偵測網路狀態的話，大家都會使用 Reachability 這套官方工具，且有 <a href="https://developer.apple.com/library/archive/samplecode/Reachability/Introduction/Intro.html" data-type="link" data-id="https://developer.apple.com/library/archive/samplecode/Reachability/Introduction/Intro.html" target="_blank" rel="noreferrer noopener">Demo Code</a> 可以直接使用，所以只要說到偵測網路第一時間一定會想到使用 Reachability。</p>



<p>在 iOS 12之後官方推出了 <a href="https://developer.apple.com/documentation/network" target="_blank" rel="noreferrer noopener">Network Kit</a> 其中有很多與網路相關的服務，期中一個就可以用來偵測網路狀態，程式碼比過去少又比較清楚，配合現在官方主推的 SwiftUI 也很好用。</p>



<span id="more-2361"></span>



<h2 class="wp-block-heading">Monitor</h2>



<h3 class="wp-block-heading">Create Publisher</h3>



<p>像這樣的Monitor 是建議獨立建立一個 Class 來處理，不要放在其他的Class 裡面</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-swift" data-lang="Swift"><code>import Network

class NetworkMonitor: ObservableObject {
    
    @Published private(set) var isConnected: Bool = false
    @Published private(set) var networkType: NWInterface.InterfaceType? = .other
}</code></pre></div>



<p>首先，記得要 <code>import Network</code>，為了能讓SwiftUI 使用記得要Conform <code>ObservableObject</code></p>



<p><code>isConnected</code> 就是在確認是否為連線狀態，<code>networkType</code> 就是屬於那一種連線類型，官方有分為 <code>wifi</code>、<code>cellular</code>…等等，詳細分類可以參考 <a href="https://developer.apple.com/documentation/network/nwinterface/interfacetype" target="_blank" rel="noreferrer noopener">NWInterface.InterfaceType</a> 的說明。</p>



<h3 class="wp-block-heading">Create Monitor</h3>



<div class="hcb_wrap"><pre class="prism line-numbers lang-swift" data-lang="Swift"><code>let networkMonitor = NWPathMonitor()
let workQueue = DispatchQueue(label: &quot;NetworkMonitor&quot;)</code></pre></div>



<p>接著往下建立 Monitor ，在Network Kit 裡有一個Class 是 <a href="https://developer.apple.com/documentation/network/nwpathmonitor" data-type="link" data-id="https://developer.apple.com/documentation/network/nwpathmonitor" target="_blank" rel="noreferrer noopener">NWPathMonitor</a> ，它就是用來偵測網路狀態的，上述這樣的寫法表示要偵測所有型態的網路，有需要的話也可以指定想要偵測的網路型態。</p>



<p>一般來說偵測網路的動作一定不可以影響到主線程的運行，因此我們需要新增一條負責偵測網路用的 <code>DispatchQueue</code>，名稱可以自訂，這裡是稱它為 <code>NetworkMonitor</code>。</p>



<h3 class="wp-block-heading">Create Path update handler</h3>



<div class="hcb_wrap"><pre class="prism line-numbers lang-swift" data-lang="Swift"><code>networkMonitor.pathUpdateHandler = { [weak self] path in
    self?.isConnected = path.status == .satisfied
    self?.networkType = path.availableInterfaces.first?.type
}</code></pre></div>



<p>當網路狀態改變的時候，可以在 <code>pathUpdateHandler</code> 這個 Closure 取得資訊，會回傳 <code>NWPath</code> 這個物件，它有網路狀態的相關資訊。</p>



<h3 class="wp-block-heading">Start</h3>



<div class="hcb_wrap"><pre class="prism line-numbers lang-swift" data-lang="Swift"><code>networkMonitor.start(queue: workQueue)</code></pre></div>



<p>最後再開始偵測，記得指定先前設定好的Queue</p>



<p>完整的程式如下</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-swift" data-lang="Swift"><code>class NetworkMonitor: ObservableObject {
    
    @Published private(set) var isConnected: Bool = false
    @Published private(set) var networkType: NWInterface.InterfaceType? = .other
    
    let networkMonitor = NWPathMonitor()
    let workQueue = DispatchQueue(label: &quot;NetworkMonitor&quot;)
    
    init() {
        
        networkMonitor.pathUpdateHandler = { [weak self] path in
            self?.isConnected = path.status == .satisfied
            self?.networkType = path.availableInterfaces.first?.type
        }
        
        networkMonitor.start(queue: workQueue)
    }
}</code></pre></div>



<h3 class="wp-block-heading">View</h3>



<p>接著就可以將這個NetworkMonitor 放到View 上使用，以下是一個簡單的範例</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-swift" data-lang="Swift"><code>struct ContentView: View {
    
    @StateObject var networkMonitor = NetworkMonitor()
    
    var body: some View {
        VStack {
            Label {
                Text(networkMonitor.isConnected ? &quot;Connected&quot; : &quot;Disconnected&quot;)
            } icon: {
                Image(systemName: networkMonitor.isConnected ? &quot;network&quot; : &quot;network.slash&quot;)
                    .foregroundStyle(networkMonitor.isConnected ? .green : .red)
            }

        }
        .padding()
    }
}</code></pre></div>



<p>當然，App 應該只會有一個 Network Monitor ，這個時候可以使用 <code>EnvironmentObject</code> 的方式來傳遞 Network Monitor，寫法可以參考以下方式，當然要從哪裡開始建立 Network Monitor 都可以依照個人需求，理論上應該是App 開啟時就需要做偵測啦，記得使用 <code>.environmentObject()</code>來讓其他畫面可以使用 <code>EnvironmentObject</code> 方式讀取到 Network Monitor。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-swift" data-lang="Swift"><code>@main
struct NetworkDemoApp: App {
    
    @StateObject var networkMonitor = NetworkMonitor()
    
    var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(networkMonitor)
        }
    }
}</code></pre></div>



<div class="hcb_wrap"><pre class="prism line-numbers lang-swift" data-lang="Swift"><code>struct ContentView: View {
    
    @EnvironmentObject var networkMonitor: NetworkMonitor
    
    var body: some View {
        VStack {
            Label {
                Text(networkMonitor.isConnected ? &quot;Connected&quot; : &quot;Disconnected&quot;)
            } icon: {
                Image(systemName: networkMonitor.isConnected ? &quot;network&quot; : &quot;network.slash&quot;)
                    .foregroundStyle(networkMonitor.isConnected ? .green : .red)
            }

        }
        .padding()
    }
}</code></pre></div>



<p>最後，祝大家 Coding 愉快!!</p>



<p></p>
]]></content:encoded>
					
					<wfw:commentRss>https://doeverythingiwant.com/ios-network-monitor/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>SwiftUI &#8211; Drop &#038; Drag List</title>
		<link>https://doeverythingiwant.com/swiftui-drop-drag-list/</link>
					<comments>https://doeverythingiwant.com/swiftui-drop-drag-list/#respond</comments>
		
		<dc:creator><![CDATA[艾普利]]></dc:creator>
		<pubDate>Sun, 09 Jun 2024 10:41:02 +0000</pubDate>
				<category><![CDATA[寫程式]]></category>
		<category><![CDATA[Drop and Drag]]></category>
		<category><![CDATA[iOS]]></category>
		<category><![CDATA[SwiftUI]]></category>
		<guid isPermaLink="false">https://doeverythingiwant.com/?p=2280</guid>

					<description><![CDATA[忘記是那一年的WWDC公布了一個拖曳相關的功能，主要是讓你可&#8230;]]></description>
										<content:encoded><![CDATA[
<p>忘記是那一年的WWDC公布了一個拖曳相關的功能，主要是讓你可以將iPhone 的資料像是圖片之類的丟到iPad 或Mac 上，因此有了 API 可以用在 Drop &amp; Drag 動作上。</p>



<p>最近因為工作需要花了一些時間在研究Drop &amp; Drag 應該要怎麼寫，需要實作的是一般的拖拉元件改變它的順序，並不需要讓它能拖到另一個裝置上，寫作上就會比較簡單一點。</p>



<span id="more-2280"></span>



<p>參考以下文件與官方網站來練習</p>



<p><a href="https://dogpa0130.medium.com/swift-practice-177-swiftui-%E6%8B%96%E6%9B%B3-drop-drag-%E6%94%B9%E8%AE%8A%E9%99%A3%E5%88%97array%E4%BD%8D%E7%BD%AE-b63ff5edb935" target="_blank" rel="noreferrer noopener">Swift Practice # 177 SwiftUI 拖曳 Drop &amp; Drag 改變陣列Array位置</a></p>



<p><a href="https://mobileappsacademy.medium.com/drag-and-drop-list-in-swiftui-30b53682d447" target="_blank" rel="noreferrer noopener">Drag and Drop List in SwiftUI</a></p>



<p><a href="https://developer.apple.com/documentation/swiftui/drag-and-drop" target="_blank" rel="noreferrer noopener">Apple Documentation &#8211; Drag and Drop</a></p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">Start</h2>



<p>一開始當然要先做一個要進行拖曳的List，建立一個顯示各種茶的List</p>



<p>建立List 的 Item View</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-swift" data-lang="Swift"><code>struct TeaItemView: View {
    
    var title: String
    
    var body: some View {
        HStack {
            Text(title)
                .font(.system(size: 26))
        }
        .frame(maxWidth: .infinity)
        .padding(.vertical, 30)
        .background(.green)
        .clipShape(RoundedRectangle(cornerRadius: 20))
        
    }
}</code></pre></div>



<p>再建立一個用 <code>ForEach</code> 做的 List </p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-swift" data-lang="Swift"><code>import SwiftUI

struct ContentView: View {
    
    @State private var draggedItem: String?
    @State private var fruitItems = [&quot;&#x1fad6; Tea&quot;, &quot; &#x2615; Coffee&quot;, &quot; &#x1f9c3; Juice&quot;, &quot;&#x1f9cb;Pearl milk tea&quot;, &quot;&#x1f95b; Milk&quot;, &quot;&#x1f379; Fruit tea&quot;, &quot;&#x1f9c9; Mate tea&quot;, &quot;&#x1f375; Matcha tea&quot;]
    
    var body: some View {
        
        ScrollView(showsIndicators: false) {
            VStack(spacing: 10) {
                ForEach(fruitItems, id: \.self) { fruit in
                    TeaItemView(title: fruit)
                }
            }
            .padding(.horizontal, 20)
        }
        .background(.black)
    }
}</code></pre></div>



<p><code>draggedItem</code> 是負責記錄要被拖曳的元件，再來Run 一下程式，會出現下面畫面</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="295" height="640" src="https://doeverythingiwant.com/wp-content/uploads/2024/06/teaList.gif" alt="" class="wp-image-2285"/></figure>
</div>


<h2 class="wp-block-heading">DrogDelegate</h2>



<p>如果要讓List 的元件可以被拖曳，必須實作 DropDelegate 這個Protocol，新增一個 DropDragDelegate 來實作它</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-swift" data-lang="Swift"><code>import SwiftUI

struct DropDrapDelegate: DropDelegate {
    
    let destinationItem: String
    
    @Binding var fruits: [String]
    @Binding var draggedItem: String?
    
    func dropUpdated(info: DropInfo) -&gt; DropProposal? {
        return DropProposal(operation: .move)
    }
    
    func performDrop(info: DropInfo) -&gt; Bool {
        draggedItem = nil
        return true
    }
    
    func dropEntered(info: DropInfo) {
        if let draggedItem {
            let fromIndex = fruits.firstIndex(of: draggedItem)
            if let fromIndex {
                let toIndex = fruits.firstIndex(of: destinationItem)
                if let toIndex, fromIndex != toIndex {
                    withAnimation {
                        self.fruits.move(fromOffsets: IndexSet(integer: fromIndex), toOffset: (toIndex &gt; fromIndex ? (toIndex + 1) : toIndex))
                    }
                }
            }
        }
    }
}</code></pre></div>



<p>在這個 Delegate 中有幾個需要實作的 function</p>



<p><code>func dropUpdated(info: DropInfo) -> DropProposal?</code>  </p>



<p>宣告這個拖曳是要什麼動作，以範例來說只是要做移動，所以是 <code>.move</code>，可以去官方文件查看 <code>DropProposal</code> 可以知道更多設定值，預設值會是 <code>nil</code>。</p>



<p><code>func performDrop(info: DropInfo) -> Bool</code></p>



<p>拖曳完成的時候會呼叫到它，回傳 <code>true</code> 表示 Drop was successful</p>



<p><code>func dropEntered(info: DropInfo)</code></p>



<p>在這個拖曳要做些什麼? 這是我對這個function 的理解，這個範例主要是要交換順序，所以裡面寫的就是交換的方式</p>



<h2 class="wp-block-heading">View</h2>



<p>再回到 View ，將 <code>OnDrop</code> 與 <code>OnDrag</code> 加到剛剛寫好的 List 中</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-swift" data-lang="Swift"><code>ScrollView(showsIndicators: false) {
    VStack(spacing: 10) {
        ForEach(fruitItems, id: \.self) { fruit in
            TeaItemView(title: fruit)
                .onDrag({
                    draggedItem = fruit
                    return NSItemProvider()
                })
                .onDrop(of: [.text], delegate: DropDrapDelegate(destinationItem: fruit, fruits: $fruitItems, draggedItem: $draggedItem))
            
        }
    }
    .padding(.horizontal, 20)
}
.background(.black)
</code></pre></div>



<p><code>onDrag</code> </p>



<p>這個的重點在於 <code>NSItemProvider</code> ，<code>NSItemProvider</code> 它是為了在進行 <code>Drag-and-drop</code> 或是 <code>Copy-and-paste</code> 時傳送資料或檔案用的，設定方式我自己是不太理解，常見的設定是 <code>URL</code>，這個範例用不到，就直接建立一個空的ItemProvider，若有需要傳送資料到不同裝置上的話應該需要留意一下。</p>



<p><code>onDrop(of:delegate:)</code></p>



<p>設定Type與Delegate ，Delegate 就是先前建立的 DropDrapDelegate，直接代需要的參考就好。</p>



<p>Type的設定有二種方式，第一種是給它 <code>The uniform type identifiers</code> ，這是需要可以辨識要傳送的資料，查了一下，應該是可以自定這個 Identifier。第二種是設定 <code>UTType</code> ，在範例中的 <code>[.text]</code> 就是 <code>UTType</code> 裡的 <code>Text</code>，在官方文件中可以看到很多種類型，那<code>UTType</code>是什麼? 它其實就是指要進行<code>load, send, or receive</code>的資料類型。</p>



<p>加上 <code>onDrag</code> 與 <code>onDrop</code> 之後就可以進行拖曳了</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="295" height="640" src="https://doeverythingiwant.com/wp-content/uploads/2024/06/Finish.gif" alt="" class="wp-image-2288"/></figure>
</div>


<p>可以在 <a href="https://github.com/AprilXMoon/DragDropDemo" target="_blank" rel="noreferrer noopener">Github</a> 上下載這個範例程式</p>



<p>最後祝大家 Coding 愉快!!</p>
]]></content:encoded>
					
					<wfw:commentRss>https://doeverythingiwant.com/swiftui-drop-drag-list/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>SwiftUI 與 Gif</title>
		<link>https://doeverythingiwant.com/swiftui-gif/</link>
					<comments>https://doeverythingiwant.com/swiftui-gif/#respond</comments>
		
		<dc:creator><![CDATA[艾普利]]></dc:creator>
		<pubDate>Sun, 04 Feb 2024 08:40:58 +0000</pubDate>
				<category><![CDATA[寫程式]]></category>
		<category><![CDATA[Gif]]></category>
		<category><![CDATA[SwiftUI]]></category>
		<guid isPermaLink="false">https://doeverythingiwant.com/?p=2191</guid>

					<description><![CDATA[Photo by Євгенія Височина on U&#8230;]]></description>
										<content:encoded><![CDATA[
<p class="has-text-align-center">Photo by <a href="https://unsplash.com/@eugenivy_now?utm_content=creditCopyText&amp;utm_medium=referral&amp;utm_source=unsplash" target="_blank" rel="noopener">Євгенія Височина</a> on <a href="https://unsplash.com/photos/a-group-of-wrapped-presents-under-a-christmas-tree-bh1fKYXv2cA?utm_content=creditCopyText&amp;utm_medium=referral&amp;utm_source=unsplash" target="_blank" rel="noopener">Unsplash</a></p>



<p>SwiftUI 本身的Image 並沒有支援 Gif 檔案，若是想要放動畫的除了用程式去寫外，現在有 Lottie 可以使用，SwiftUI 其實也是可以使用 Gif Image ，不過畢需要透過WKWebView 來實現</p>



<span id="more-2191"></span>



<p>先找一個gif 圖，本次使用的是這個櫻花的圖示</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="256" height="256" src="https://doeverythingiwant.com/wp-content/uploads/2023/12/cherry-blossoms.gif" alt="" class="wp-image-2193"/><figcaption class="wp-element-caption">GIF by <a href="https://pixabay.com/users/viaductk-34217157/?utm_source=link-attribution&amp;utm_medium=referral&amp;utm_campaign=animation&amp;utm_content=6383" target="_blank" rel="noopener">viaductk</a> from <a href="https://pixabay.com//?utm_source=link-attribution&amp;utm_medium=referral&amp;utm_campaign=animation&amp;utm_content=6383" target="_blank" rel="noopener">Pixabay</a></figcaption></figure>
</div>


<p>這個檔案需要放在專案裡面，不需要放在 Assets 因為它並不是單純的圖片</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="454" height="388" src="https://doeverythingiwant.com/wp-content/uploads/2023/12/000.png" alt="" class="wp-image-2194" srcset="https://doeverythingiwant.com/wp-content/uploads/2023/12/000.png 454w, https://doeverythingiwant.com/wp-content/uploads/2023/12/000-300x256.png 300w" sizes="(max-width: 454px) 100vw, 454px" /></figure>
</div>


<p><code>WKWebView</code> 是UIView 現在屬於WebKit 底下，UIView 是屬於 UIKit 底下，而UIKit元件要使用在 SwiftUI 上需要用<code>UIViewRepresentable</code> 做處理，官方有一個<a href="https://developer.apple.com/tutorials/swiftui/interfacing-with-uikit" target="_blank" rel="noreferrer noopener">教學</a>，有興趣的可以看一看，最主要就是需要實作二個function</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-swift" data-lang="Swift"><code>func makeUIView(context: Context) -&gt; UIView { }
func updateUIView(_ uiView: UIView, context: Context) { }</code></pre></div>



<p>在有<code>Delegate</code> 的情況就是要實作 <code>class Coordinator</code>，詳細可以看教學或是上網找一下相關教學</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">實作</h2>



<p>這次主要使用的是 <code>WKWebView</code> 因此上述 <code>UIView</code> 的部分都需要改成 <code>WKWebView</code></p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-swift" data-lang="Swift"><code>func makeUIView(context: Context) -&gt; WKWebView {
    let webView = WKWebView()
    let url = Bundle.main.url(forResource: imageName, withExtension: &quot;gif&quot;)!
    let data = try! Data(contentsOf: url)
        
    webView.load(data, mimeType: &quot;image/gif&quot;, characterEncodingName: &quot;UTF-8&quot;, baseURL: url.deletingLastPathComponent())
        
    return webView
}

func updateUIView(_ uiView: WKWebView, context: Context) {
    uiView.reload()
}</code></pre></div>



<p>剛剛有說過 <code>WKWebView</code> 是屬於 WebKit 底下，所以要記得 <code>import WebKit</code>，簡單說明一下程式碼</p>



<ul class="wp-block-list">
<li>建立一個 <code>WKWebView</code> 做為回傳值</li>



<li><code>url</code> 參數取得gif 位置，<code>forResource</code> 是代入圖片名稱，因為希望能任意填入名稱，所以使用一個參數代表 image name，<code>Bundle.main.url()</code> 的回傳值是 Optional ，由於Gif 圖示一定要放在檔案裡，基本上不會有nil 的情況，所以就直接最後加上 !</li>



<li><code>data</code> 參數將圖片轉成 data，回傳值也是Optional的，如上述所說，這裡也直接使用 <code>try!</code> 來處理</li>



<li>最後讓webView 去取讀這個檔案
<ul class="wp-block-list">
<li><code>mimeType</code> 就是指 Internet media type，寫web 的朋友們應該都很清楚，這是用來表示這是什麼樣的檔案，在此當然是 &#8220;image/gif&#8221;，有興趣可以看一下<a href="https://zh.wikipedia.org/zh-tw/%E4%BA%92%E8%81%94%E7%BD%91%E5%AA%92%E4%BD%93%E7%B1%BB%E5%9E%8B" target="_blank" rel="noreferrer noopener">維基百科</a>的說明</li>



<li><code>characterEncodingName</code>，使用 UTF-8 來做解碼方式</li>
</ul>
</li>



<li><code>updateUIView()</code> 只需要做一件事，就是 <code>reload()</code> 就好了</li>
</ul>



<p>這樣就完成啦~ 很簡單對吧~ </p>



<p>最後的結果就像這樣，加上 .frame 就可以調整大小，使用上非常方便</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-swift" data-lang="Swift"><code>VStack {
   GifImageView(imageName: &quot;cherry-blossoms&quot;)
       .frame(width: 60, height: 60)
   Text(&quot;Hello, world!&quot;)
}
.padding()</code></pre></div>


<div class="wp-block-image wp-duotone-unset-1">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="295" height="640" src="https://doeverythingiwant.com/wp-content/uploads/2023/12/demo.gif" alt="" class="wp-image-2199"/></figure>
</div>


<p>不知道未來 SwiftUI 會不會發展出自己的 WebView ，或是 Image 之後也可以支援 Gif 檔案，在那之前就用 WKWebView 來呈現Gif 圖示吧</p>



<p>最後祝大家 Coding 愉快!!</p>



<p>可以在<a href="https://github.com/AprilXMoon/GifImage" target="_blank" rel="noreferrer noopener">這裡</a>取得此Demo 檔案喔~</p>
]]></content:encoded>
					
					<wfw:commentRss>https://doeverythingiwant.com/swiftui-gif/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>[SwiftUI] Remote Image 讀取方式</title>
		<link>https://doeverythingiwant.com/swiftui-remote-image/</link>
					<comments>https://doeverythingiwant.com/swiftui-remote-image/#respond</comments>
		
		<dc:creator><![CDATA[艾普利]]></dc:creator>
		<pubDate>Sun, 28 Aug 2022 08:31:20 +0000</pubDate>
				<category><![CDATA[寫程式]]></category>
		<category><![CDATA[iOS]]></category>
		<category><![CDATA[SwiftUI]]></category>
		<guid isPermaLink="false">https://doeverythingiwant.com/?p=29</guid>

					<description><![CDATA[Photo by BRUNO EMMANUELLE on U&#8230;]]></description>
										<content:encoded><![CDATA[
<p class="has-text-align-center">Photo by <a href="https://unsplash.com/@brunocervera?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" target="_blank" rel="noopener">BRUNO EMMANUELLE</a> on <a href="https://unsplash.com/s/photos/mac?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" target="_blank" rel="noopener">Unsplash</a></p>



<p>製作應用型App 最常遇到的就是必須從 URL 讀取圖片，雖然可以使用<em>URLSession</em> 去處理，但是實在是太麻煩了，所以一般都會使用第三方程式，此在外iOS15之後，Apple 官方也推出了一個新的Image 元件是專門來讀取Remote Image 的，這篇文章會介紹 一直在使用的 <strong>SDWebImage</strong> 與官方推出的<strong>AsyncImage</strong>。</p>



<p></p>



<span id="more-29"></span>



<p>先從大家比較熟知的 <strong><a href="https://github.com/SDWebImage" target="_blank" rel="noopener">SDWebImage</a></strong> 談起吧</p>



<p id="dbdb">相信 iOS Developer 應該是無人不知，無人不曉，說到好用的Remote image third party，<strong>SDWebImage</strong> 確定是名單上的第一名，從 Objc 時代到用SwiftUI 都很好用，且也推出了很多 Coder，幾乎可以支援到所有圖片類型，是相當好用的一個套件。</p>



<p id="876a">使用方式在Github 上與網路上都有介紹，這裡就不細談，簡單來說就是載入所需要的 <strong>SDWebImage</strong> 套件，放到專案裡，它有支援各種第三方套件的管理程式， <strong>CocoaPods</strong>、<strong>Carthage</strong>及 <strong>Swift Package Manager</strong>，只要依照說明就可以很方便的載入專案裡</p>



<p id="7ff6">由於iOS 也從Objc演變到 Swift ， UIKit演變到 SwiftUI，因此要載入那個<strong>SDWebImage</strong>套件就看你App使用的是什麼</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p id="f8d0"><strong>Objc / Swift + UIKit</strong></p>



<p id="07f9">使用的套件是<strong> SDWebImage </strong>，其使用方式是<strong> import SDWebImage</strong> 後呼叫<strong>sd_setImage</strong></p>



<p id="2fda">．Objective-C</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-objectivec" data-lang="Objective-C"><code>#import &lt;SDWebImage/SDWebImage.h&gt;
...
[imageView sd_setImageWithURL:[NSURL URLWithString:@&quot;http://www.domain.com/path/to/image.jpg&quot;]placeholderImage:[UIImage imageNamed:@&quot;placeholder.png&quot;]];
...</code></pre></div>



<p id="996a">．Swift</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-swift" data-lang="Swift"><code>import SDWebImage
...
imageView.sd_setImage(with: URL(string: &quot;http://www.domain.com/path/to/image.jpg&quot;), placeholderImage: UIImage(named: &quot;placeholder.png&quot;))
...</code></pre></div>



<p id="b6d8"><strong>SwiftUI</strong></p>



<p id="9b73">使用的套件是 <a href="https://github.com/SDWebImage/SDWebImageSwiftUI" target="_blank" rel="noopener">SDWebImageSwiftUI</a></p>



<p id="256b">使用方式是<strong>import SDWebImageSwiftUI</strong> 之後呼叫 <strong>WebImage</strong></p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-swift" data-lang="Swift"><code>WebImage(url: URL(string: &quot;http://www.domain.com/path/to/image.jpg&quot;)</code></pre></div>



<p id="fb50">當然它也有很多 <strong>Modifier</strong> 可以使用，可以直接看 Github 的使用說明</p>



<p id="dd1c">剛剛有說到它也出了一些Coder ，主要是為了支援原本<strong>SDWebImage</strong> 沒有支搜的圖片類型，像最近常遇到的就是 SVG 的圖檔，SVG 是向量圖的一種，在使用圖片的時候最怕的就是圖片變形，因為手機的解析度各有不同就算是iPhone 也已經出很多不同解析度的手機，更別說Android 手機了，只要圖片比例沒有處理好，就很容易變形，向量圖就不會有這種問題，但是一般Size 都會大一點。</p>



<p id="1ea8">SVG 這個圖檔，目前iOS的 <strong>Image</strong> 已經有支援，雖然不知道是那一版開始支援的，不過若SVG 的圖檔是直接放在專案裡的話，可以直接使用Image元件來讀取圖檔</p>



<p id="206e">那若是必須要使用URL讀取SVG 圖檔的話，<strong>SDWebImage</strong> 就有提供SVG 的Coder 就叫 <a rel="noreferrer noopener" href="https://github.com/SDWebImage/SDWebImageSVGCoder" target="_blank">SDWebImageSVGCoder</a></p>



<p id="73c6">使用方式也很簡單，當然要載入 <strong>SDWebImageSVGCoder</strong><em> </em>與 <strong>import</strong><em> </em><strong>SDWebImageSVGCoder</strong>，然後到再 <strong>AppDelegate</strong> 的<strong>didFinishLaunchingWithOptions</strong><em> </em>裡寫上</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-swift" data-lang="Swift"><code>let SVGCoder = SDImageSVGCoder.shared
SDImageCodersManager.shared.addCoder(SVGCoder)</code></pre></div>



<p>若是使用<strong>SwiftUI</strong>的話也可以在 Struct App 的 init 中去呼叫 <strong>SDImageCodersManager.shared.addCoder(SVGCoder)</strong><em> ，</em>可以參考下列文章</p>



<p><a href="https://anoop4real.medium.com/display-svg-in-swiftui-ios-watchos-260120557e3a" target="_blank" rel="noopener">Display svg in swiftUI ios</a></p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-swift" data-lang="Swift"><code>import SwiftUI
import SDWebImage
import SDWebImageSVGCoder

@main
struct demoApp: App {
    init() {
      // Initialize SVGCoder
       SDImageCodersManager.shared.addCoder(SDImageSVGCoder.shared) 
    }
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}</code></pre></div>



<p><strong>Objc</strong> / <strong>Swift</strong> 一樣是呼叫<strong>sd_setImage</strong><em>，</em><strong>SwiftUI</strong><em> 就是 </em><strong>WebImage</strong></p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p id="1e26">最後來說說 iOS 15之後推出的 <a href="https://developer.apple.com/documentation/swiftui/asyncimage" target="_blank" rel="noreferrer noopener">AsyncImage</a></p>



<p id="01a1">原本需要使用 <code><a rel="noreferrer noopener" href="https://developer.apple.com/documentation/foundation/urlsession" target="_blank">URLSession</a></code> 才可以讀取 Remote Image 且建置十分麻煩，所以<em>SDWebImage</em> 才會這麼多人愛用，但是到了iOS15就有新的選擇了，就是 <strong>AsyncImage !</strong></p>



<p id="1762">使用方式也是非常簡單只要呼叫<em> </em><strong>AsyncImage</strong><em> </em>就可以輕輕鬆鬆讀取到Remote Image</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-swift" data-lang="Swift"><code>AsyncImage(url: URL(string: &quot;https://example.com/icon.png&quot;))
    .frame(width: 200, height: 200)</code></pre></div>



<p id="fa6a">而且，它還提供了placeholder 的部分，可以像官方寫的使用<code>ProgressView</code></p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-swift" data-lang="Swift"><code>AsyncImage(url: URL(string: &quot;https://example.com/icon.png&quot;)) { image in
    image.resizable()
} placeholder: {
    ProgressView()
}
.frame(width: 50, height: 50)</code></pre></div>



<p id="d9cb">那在圖片讀到之前它都會顯示<code>ProgressView</code> ，若想要寫那個不知道為什麼很多App 愛用的 <em>Shimmer </em>效果也是可以的</p>


<div class="wp-block-image is-style-default">
<figure class="aligncenter"><img decoding="async" src="https://miro.medium.com/max/592/1*l62dQtb1X37S663VQkZH3A.gif" alt=""/><figcaption class="wp-element-caption">Shimmer</figcaption></figure>
</div>


<div class="hcb_wrap"><pre class="prism line-numbers lang-swift" data-lang="Swift"><code>AsyncImage(url: URL(string:&quot;https://example.com/icon.png&quot;)) 
    { image in
          image
             .resizable()
     } placeholder: {
           Color.gray
              .shimmering()
     }
     .frame(maxWidth: .infinity, maxHeight: 350)</code></pre></div>



<p id="0234">至於要怎麼寫出<strong>Shimmer</strong> 效果，這個Google 一下就查得到了，在這裡就不特別說明</p>



<p id="adf1">雖然官方在SwiftUI 上已經有推出很好用的 <strong>AsyncImage</strong> 不過可惜的是它並不支援SVG 圖檔，現在應該只有支援 PNG/JPG 圖檔，相信未來它應該也會支援更多的圖片格式</p>



<p id="03c4">不過大多數的情況下Remote Image應該都是 PNG/JPG的圖檔，SVG 到是比較少見，若有特別需求的話，其實可以使用SDWebImage來處理的</p>



<p id="968e">最後~祝大家 Coding 愉快~</p>
]]></content:encoded>
					
					<wfw:commentRss>https://doeverythingiwant.com/swiftui-remote-image/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
