技術筆記-iOS實戰003-取用SwiftUI之必要元件,搭配資料處理技術,組成一個有意義的應用

閱讀時間約 13 分鐘

需求情境:

一般的看盤軟體,雖然都能針對一籃子自選股票,列出其即時行情和當天漲幅,但若要看「五日漲幅」呢?那就少見了,但這對我很重要。因為小部位的波段性價差交易是個好策略,這時候若能排序好一整排看下來,可以節省大量點來點去的成本,很有價值,所以就來自己刻。

解決方案:

從大處著眼,UI 最外層需要一個可上下捲動的容器,就是 ScrollView;容器裡面放一個類似表格的結構。雖然 SwiftUI 有一個 Table 元件,但它預設是給 iPad 使用的,在iPhone 上顯示會很奇怪,只會顯示一行,也不會有標題,然後沒有任何提示說資料顯示不完全,簡直是莊孝維,完全不可用,故放棄之,改用 Grid 取代之。資料排列整齊之後,希望能夠按標題排序,切換正序或倒序。另外也須希望能夠切換觀察漲幅的基準是一日或五日,所以又會用到 Picker 這種元件,然後取用上一篇 iOS實戰002 裡面資料處裡的技術成果,搭配起來,讓功能水準得到一次顯著提升。

實作步驟:

最完整的文件就在原始碼裡面。以下程式重點為,以 ScrollView and Grid 為最外層容器,裡面一行一行以 GridRow 依序展開。第一行為標題,三個欄位固定只有文字,第四個欄位就比較特別了,是由一個 Button 組成,點下去會排序,排序完後順便把控制排序順序的變數 sortDirection 變號,使下一次再按時變成順序顛倒。

接下來跑一個迴圈,每一筆資料產生一個 GridRow,裡面包含四個欄位的資料格內容,為了讓每一格顯示上下兩項資料,所以又用 VStack 包起來。第一格的 Symbol 部分,用之前系統設計既有風格的自訂顏色,最後一個則動態指定顏色,漲時紅色,跌時綠色,事就這樣成了。

ScrollView {
Grid {
GridRow() {
Text("Symbol")
Text("Base Day")
Text("Price")
HStack {
Button(action: {
self.newDashItems.sort(by: {
a, b in
a.chgRate*sortDirection < b.chgRate*sortDirection
})
sortDirection *= (-1.0)
},
label: {
Text("Change")
Image(systemName: "chevron.up.chevron.down")
})
}
}
.font(.headline)
.fontWeight(.bold)
.background()

Divider()

ForEach(newDashItems) { item in
GridRow {
//DashItemView(item: item)
VStack {
let symbolStr = item.symbol.replacingOccurrences(of: ".TWO", with: "").replacingOccurrences(of: ".TW", with: "")
Text(symbolStr)
.padding(3)
.foregroundColor(Color(red: 181/255.0, green: 81/255.0, blue: 81/255.0))
.background(Color(red: 242/255.0, green: 244/255.0, blue: 244/255.0), in: RoundedRectangle(cornerRadius: 8))
.fontWeight(Font.Weight.bold)
.padding(3)
Text("\(item.name)")
.fontWeight(.bold)
}
VStack {
Text(item.baseDay.suffix(4))
let baseCloseString = String(format: "%.2f", item.baseClose)
Text(baseCloseString)
}
VStack {
let lastTimeValue = item.dataList[item.dataList.count-1]
Text(lastTimeValue.time.suffix(9))
let newValueString = String(format: "%.2f", item.dataList[item.dataList.count-1].value)
HStack {
Text("\(newValueString)")
}
}
VStack {
let iconColor = item.chgRate > 0 ? Color.red : Color.green
let chgRateStr = String(format: "%.1f", item.chgRate*100)
Text("\(chgRateStr) %")
.foregroundColor(iconColor)
}
}
Divider()
}
}
}
.onAppear {
getDashItemsFromFile()
}

以下兩畫面分別顯示一日漲幅與五日漲幅,格式均相同,只是 Base Day 的內容被抽換了,後面繼續說明資料處理部分的程式。

raw-image

按下 Picker day1 or day5 的動作如下:

先宣告狀態變數 baseDayOption,屬於自訂列舉型別 BaseDayOption,具有預設值 day1,按下按鈕時,將原始從後台接收的 dashItems 用 map 函數映射成另一個陣列 newDashItems,根據選項重先設定 baseDay, baseClose, chgRate,UI 元件則設定好會顯示新的陣列。

@State private var baseDayOption: BaseDayOption = .day1
enum BaseDayOption {
case day1
case day5
}

func computeNewItems() {
switch baseDayOption {
case .day1:
newDashItems = dashItems.map { item in
var newItem = item
newItem.baseDay = item.lastCloseDay
newItem.baseClose = item.lastClose
newItem.chgRate = (item.dataList[item.dataList.count-1].value - item.lastClose) / item.lastClose
return newItem
}
case .day5:
newDashItems = dashItems.map { item in
var newItem = item
newItem.baseDay = item.last5Day
newItem.baseClose = item.last5Close
newItem.chgRate = (item.dataList[item.dataList.count-1].value - item.last5Close) / item.last5Close
return newItem
}
}
}

因為 baseDay,baseClose,chgRate 三個欄位是計算欄位,後台傳回時並沒有,因此在 decode 時會出錯,因此必須重新定義初始化函示 init(),在其中指定預設值,讓 decoder 可順利運作:

struct DashItem: Codable, Identifiable {
    let alertId: Int
    let symbol: String 
    let name: String
    let lastCloseDay: String
    let last5Day: String
    let lastClose: Double
    let last5Close: Double
    let dataList: [TimeValue]
    var id: Int {
        alertId
    }
    
    var baseDay: String
    var baseClose: Double
    var chgRate: Double
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        alertId = try container.decode(Int.self, forKey: .alertId)
        symbol = try container.decode(String.self, forKey: .symbol)
        name = try container.decode(String.self, forKey: .name)
        lastCloseDay = try container.decode(String.self, forKey: .lastCloseDay)
        last5Day = try container.decode(String.self, forKey: .last5Day)
        lastClose = try container.decode(Double.self, forKey: .lastClose)
        last5Close = try container.decode(Double.self, forKey: .last5Close)
        dataList = try container.decode([TimeValue].self, forKey: .dataList)
        baseDay = ""
        baseClose = 0
        chgRate = 0
    }
}

struct TimeValue: Codable {
    let time: String
    let value: Double
}

至於系統增加顯示檔案更新的時間,使用以下程式:

func getFileDatetime(fileName: String) -> Date? {
let filePath = getDocumentDirectory().appendingPathComponent(fileName)
do {
let attributes = try FileManager.default.attributesOfItem(atPath: filePath.path)
if let modificationDate = attributes[FileAttributeKey.modificationDate] as? Date {
return modificationDate
} else {
return nil
}
} catch {
return nil
}
}

技術掌握狀況盤點:

資料部分:

  1. 呼叫 REST api,接收 json 資料並解碼,特定欄位不解碼,塞預設值。
  2. 非同步呼叫,程序獨立成 service 模組,並非同步接收回傳值與 UI 渲染。
  3. 陣列的 map 操作,sort 操作。
  4. 本地端 DocumentDirectory 裡的檔案儲存,讀取。

UI 部分:

  1. Layout:ScrollView,GridView,VStack,HStack
  2. 元件:Text,Image,Button,Picker


Newman 2024/6/20

導覽頁:紐曼的技術筆記-索引









11會員
95內容數
漫步,悠閒自在的隨意行走!是行為,是態度,也是一種境界。
留言0
查看全部
發表第一個留言支持創作者!
newman的沙龍 的其他內容
需求情境: 在設計畫面時,資料來源是後台的 api,每一次畫面細節的修修改改,都會觸發 Xcode Preview 程序,導致不斷呼叫後台。此時若資料結構和大小都具有一定規模,就會導致效率低落,不斷等待,且消耗伺服器資源甚鉅。 解決方案: 將後台傳回的資料以檔案形式暫存在本地端,每次 pr
Part.1 搞定基本的 UI 開始開發 iOS App。 首先準備一台 Mac,然後安裝 Xcode,新增專案,系統即刻生成基本的專案結構。coding 的起點在檔案 ContentView.swift: import SwiftUI struct ContentView: View {  
技術筆記-用 python 操作 google firestore 的基本方法 (2023/12/26) 技術筆記-以 nodejs 為後台,以 google sheet 充當資料庫 (2022/11/29)
firestore 是 google 所提供的雲端文件式資料庫服務,為各種開發工具提供了方便使用的 sdk,python 的套件名稱為 firebase-admin,用 pip 安裝後就可操作了。 pip install firebase-admin
安裝完成 nodejs 後選用一個工作目錄執行 npm init,npm 會產生一個 package.json 檔案,之後為此專案安裝套件時都會記錄在此,讓專案可以很容易的重建和移植,也可設定 npm start 執行時以哪一個 js 檔當作系統入口。直接開寫了,以下我用 app.js 當作系統入口
首先安裝 python,依照 官網 的指示,下載正確的版本並執行就可,重點在以下。 虛擬環境的管理平台許多人使用 anaconda,許多教 python 的課程第一堂就是不管三七二十一先安裝再說,但我用了一陣子之後覺得它實在太笨重了,早有掙脫的想法,欣見原生的 python 已經具有虛擬環境管理模
需求情境: 在設計畫面時,資料來源是後台的 api,每一次畫面細節的修修改改,都會觸發 Xcode Preview 程序,導致不斷呼叫後台。此時若資料結構和大小都具有一定規模,就會導致效率低落,不斷等待,且消耗伺服器資源甚鉅。 解決方案: 將後台傳回的資料以檔案形式暫存在本地端,每次 pr
Part.1 搞定基本的 UI 開始開發 iOS App。 首先準備一台 Mac,然後安裝 Xcode,新增專案,系統即刻生成基本的專案結構。coding 的起點在檔案 ContentView.swift: import SwiftUI struct ContentView: View {  
技術筆記-用 python 操作 google firestore 的基本方法 (2023/12/26) 技術筆記-以 nodejs 為後台,以 google sheet 充當資料庫 (2022/11/29)
firestore 是 google 所提供的雲端文件式資料庫服務,為各種開發工具提供了方便使用的 sdk,python 的套件名稱為 firebase-admin,用 pip 安裝後就可操作了。 pip install firebase-admin
安裝完成 nodejs 後選用一個工作目錄執行 npm init,npm 會產生一個 package.json 檔案,之後為此專案安裝套件時都會記錄在此,讓專案可以很容易的重建和移植,也可設定 npm start 執行時以哪一個 js 檔當作系統入口。直接開寫了,以下我用 app.js 當作系統入口
首先安裝 python,依照 官網 的指示,下載正確的版本並執行就可,重點在以下。 虛擬環境的管理平台許多人使用 anaconda,許多教 python 的課程第一堂就是不管三七二十一先安裝再說,但我用了一陣子之後覺得它實在太笨重了,早有掙脫的想法,欣見原生的 python 已經具有虛擬環境管理模
你可能也想看
Google News 追蹤
Thumbnail
這個秋,Chill 嗨嗨!穿搭美美去賞楓,裝備款款去露營⋯⋯你的秋天怎麼過?秋日 To Do List 等你分享! 秋季全站徵文,我們準備了五個創作主題,參賽還有機會獲得「火烤兩用鍋」,一起來看看如何參加吧~
大家是否有撰寫的心血因為不明原因,網路異常而造成心血全都消失,這篇純粹是一個抒發文...
Thumbnail
提到「做筆記」你會想到什麼?如果說知識的消化可以分為「輸入、處理和輸出」三個階段,那麼你又認為筆記扮演的任務角色會落到哪一個階段?做筆記只是「輸入」成效不高,想要「輸出」又耗時費力。要如何解決這個問題呢?卡片盒筆記法,就是目前最有效的解方,特別是在數位工具智能化之後,更顯得這套方法的強大。
Thumbnail
數位筆記術的改變力量 您有曾經煩惱過學習的效率嗎?或者是覺得難以捕捉那些稍縱即逝的靈感?在我們的日常生活和學習中,這些問題都是常見的挑戰。但幸運的是,有一種工具可以幫助我們改變這些情況,那就是數位筆記術。接下來,我將會講解三個使用數位筆記術的重要原則,這些原則能幫助我們更有效地學習和捕捉靈感。 原則
Thumbnail
《花漾年華》四連幅是我用當代水墨的抽象語彙,表現臺灣春天茂盛的新綠,充滿生命力,山櫻花盛開。傳統花鳥畫的創作哲學是「一花一世界,一葉一如來」,透過描繪大自然的生機呈現藝術家空靈的精神世界,心靈與自然合一,既超越自然又貼近自然。 在手工宣紙,揮灑充滿想像力的純淨世界,留白,紓解現代人過量的影音刺激。
Thumbnail
2014年人生峰迴路轉,從一個藝術愛好者,開始投身書畫創作,九個月後受邀舉辦第一次個展。江心靜透過墨彩融合的技法展現內在生命能量,在堆疊墨韻、鮮明色彩和象徵性符號中,營造藝術與靈性追求的意境,優遊於具象和抽象水墨間。 我用畫畫寫詩,好的詩打破既有窠臼,以全新角度看待人生,詩有無限可能,可以多重解讀。
Thumbnail
藝術家江心靜投入創作第二年,內在巨大無比的能量爆發,完成了 2015《藍色空間》三連幅當代水墨創作。 詩人的心找到了視覺語言,用深邃迷離的藍,與宇宙的能量結合,與早年創作的詩句呼應,內外合一的文人走向天命之路。
Thumbnail
雲海流動如一首神秘的歌,興起一股衝動,想透過創作表達心中感悟,第一個想法是強調「氣韻生動」的水墨,2012年採訪現代水墨之父劉國松,高齡八十的畫家精神奕奕,親自示範技法。 看著成品,在廚房燈下微笑,黑白構成的畫面,有一股力量,從山間飛到城市,推動著自己繼續,不在意繁華如夢,一步一步,往前走。
Thumbnail
兩年多前,開始有了重新架構網站的想法,所以把 Blogger 架設的網站,移轉至到了新的平台,當年我也做過了一些優缺點評析,剛好最近又再重新整理網站的所有架構,順便也重新啟動了一次 Hugo 架設網站的循環。
Thumbnail
因為要轉換一份新工作,認為自己在工作上必需更提高效率,剛好週圍有些朋友使用電子筆記,對於喜歡寫紙本筆記的人來說,是一個新的嘗試,原因有二:其一,筆記上有很多資料,在用完整本筆記之後,隨之石沉大海;其二,現在越來越多資訊是透過網路傳送,如果沒有好好收藏,資料就"放在"硬碟內了。 我是先去Youtube
Thumbnail
這個秋,Chill 嗨嗨!穿搭美美去賞楓,裝備款款去露營⋯⋯你的秋天怎麼過?秋日 To Do List 等你分享! 秋季全站徵文,我們準備了五個創作主題,參賽還有機會獲得「火烤兩用鍋」,一起來看看如何參加吧~
大家是否有撰寫的心血因為不明原因,網路異常而造成心血全都消失,這篇純粹是一個抒發文...
Thumbnail
提到「做筆記」你會想到什麼?如果說知識的消化可以分為「輸入、處理和輸出」三個階段,那麼你又認為筆記扮演的任務角色會落到哪一個階段?做筆記只是「輸入」成效不高,想要「輸出」又耗時費力。要如何解決這個問題呢?卡片盒筆記法,就是目前最有效的解方,特別是在數位工具智能化之後,更顯得這套方法的強大。
Thumbnail
數位筆記術的改變力量 您有曾經煩惱過學習的效率嗎?或者是覺得難以捕捉那些稍縱即逝的靈感?在我們的日常生活和學習中,這些問題都是常見的挑戰。但幸運的是,有一種工具可以幫助我們改變這些情況,那就是數位筆記術。接下來,我將會講解三個使用數位筆記術的重要原則,這些原則能幫助我們更有效地學習和捕捉靈感。 原則
Thumbnail
《花漾年華》四連幅是我用當代水墨的抽象語彙,表現臺灣春天茂盛的新綠,充滿生命力,山櫻花盛開。傳統花鳥畫的創作哲學是「一花一世界,一葉一如來」,透過描繪大自然的生機呈現藝術家空靈的精神世界,心靈與自然合一,既超越自然又貼近自然。 在手工宣紙,揮灑充滿想像力的純淨世界,留白,紓解現代人過量的影音刺激。
Thumbnail
2014年人生峰迴路轉,從一個藝術愛好者,開始投身書畫創作,九個月後受邀舉辦第一次個展。江心靜透過墨彩融合的技法展現內在生命能量,在堆疊墨韻、鮮明色彩和象徵性符號中,營造藝術與靈性追求的意境,優遊於具象和抽象水墨間。 我用畫畫寫詩,好的詩打破既有窠臼,以全新角度看待人生,詩有無限可能,可以多重解讀。
Thumbnail
藝術家江心靜投入創作第二年,內在巨大無比的能量爆發,完成了 2015《藍色空間》三連幅當代水墨創作。 詩人的心找到了視覺語言,用深邃迷離的藍,與宇宙的能量結合,與早年創作的詩句呼應,內外合一的文人走向天命之路。
Thumbnail
雲海流動如一首神秘的歌,興起一股衝動,想透過創作表達心中感悟,第一個想法是強調「氣韻生動」的水墨,2012年採訪現代水墨之父劉國松,高齡八十的畫家精神奕奕,親自示範技法。 看著成品,在廚房燈下微笑,黑白構成的畫面,有一股力量,從山間飛到城市,推動著自己繼續,不在意繁華如夢,一步一步,往前走。
Thumbnail
兩年多前,開始有了重新架構網站的想法,所以把 Blogger 架設的網站,移轉至到了新的平台,當年我也做過了一些優缺點評析,剛好最近又再重新整理網站的所有架構,順便也重新啟動了一次 Hugo 架設網站的循環。
Thumbnail
因為要轉換一份新工作,認為自己在工作上必需更提高效率,剛好週圍有些朋友使用電子筆記,對於喜歡寫紙本筆記的人來說,是一個新的嘗試,原因有二:其一,筆記上有很多資料,在用完整本筆記之後,隨之石沉大海;其二,現在越來越多資訊是透過網路傳送,如果沒有好好收藏,資料就"放在"硬碟內了。 我是先去Youtube