技術筆記-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

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









留言
avatar-img
留言分享你的想法!
avatar-img
newman的沙龍
24會員
117內容數
漫步是一種境界。
newman的沙龍的其他內容
2025/04/01
Reinforcement Learning (強化學習) 的理論非常有趣,可能是因為其中許多方法,與人類的學習歷程極為相似,如試錯,獎懲,改進策略,持續優化等等。現在準備來爬這座山了,我把學習階段大致分成三個小山峰,依序為 Q-Learning --> DQN --> Actor-Critic,
Thumbnail
2025/04/01
Reinforcement Learning (強化學習) 的理論非常有趣,可能是因為其中許多方法,與人類的學習歷程極為相似,如試錯,獎懲,改進策略,持續優化等等。現在準備來爬這座山了,我把學習階段大致分成三個小山峰,依序為 Q-Learning --> DQN --> Actor-Critic,
Thumbnail
2025/03/08
稍微看一下 Telegram 官方文件,哇!好強喔,功能說明的第一項赫然出現「可以取代整個網站」!口氣真的很大。雖然我的需求應該很低,但能夠確認前面是一座豐富的寶藏,還是很令人興奮的,待基本功能掌握之後,可以再評估和決定要不要往下挖。 發送訊息 要達成這第一個目標,首先必須建立一個 bot。
Thumbnail
2025/03/08
稍微看一下 Telegram 官方文件,哇!好強喔,功能說明的第一項赫然出現「可以取代整個網站」!口氣真的很大。雖然我的需求應該很低,但能夠確認前面是一座豐富的寶藏,還是很令人興奮的,待基本功能掌握之後,可以再評估和決定要不要往下挖。 發送訊息 要達成這第一個目標,首先必須建立一個 bot。
Thumbnail
2025/03/01
Line Notify 即將停止服務,隨著時間越來越緊迫,隱約聽到許多人在哀嚎。印象中有許多廠商,把 Line Notify 用得淋漓盡致,甚至可以一個客戶建一個群組,把許多客製化服務都用程式管理的井井有條,得到很好的滿意度。但這種好康,無限免費的即時訊息,沒有了,時間就在 2025/3/31!公告
Thumbnail
2025/03/01
Line Notify 即將停止服務,隨著時間越來越緊迫,隱約聽到許多人在哀嚎。印象中有許多廠商,把 Line Notify 用得淋漓盡致,甚至可以一個客戶建一個群組,把許多客製化服務都用程式管理的井井有條,得到很好的滿意度。但這種好康,無限免費的即時訊息,沒有了,時間就在 2025/3/31!公告
Thumbnail
看更多
你可能也想看
Thumbnail
介紹朋友新開的蝦皮選物店『10樓2選物店』,並分享方格子與蝦皮合作的分潤計畫,註冊流程簡單,0成本、無綁約,推薦給想增加收入的讀者。
Thumbnail
介紹朋友新開的蝦皮選物店『10樓2選物店』,並分享方格子與蝦皮合作的分潤計畫,註冊流程簡單,0成本、無綁約,推薦給想增加收入的讀者。
Thumbnail
當你邊吃粽子邊看龍舟競賽直播的時候,可能會順道悼念一下2300多年前投江的屈原。但你知道端午節及其活動原先都與屈原毫無關係嗎?這是怎麼回事呢? 本文深入探討端午節設立初衷、粽子、龍舟競渡與屈原自沉四者。看完這篇文章,你就會對端午、粽子、龍舟和屈原的四角關係有新的認識喔。那就讓我們一起解開謎團吧!
Thumbnail
當你邊吃粽子邊看龍舟競賽直播的時候,可能會順道悼念一下2300多年前投江的屈原。但你知道端午節及其活動原先都與屈原毫無關係嗎?這是怎麼回事呢? 本文深入探討端午節設立初衷、粽子、龍舟競渡與屈原自沉四者。看完這篇文章,你就會對端午、粽子、龍舟和屈原的四角關係有新的認識喔。那就讓我們一起解開謎團吧!
Thumbnail
重視籌碼分析 基本面跟技術面需要去研究線圖指標或財報等財務資訊 但籌碼只要花點時間研究或許就能看出端倪 分點解析搭配技術型態也常能搭上主力的順風車 跟著勝利券商操作 勝率也能大大提升! 改變一下分享的個股 即便如此 停損停利的重要性還是要有 萬一不如預期 一定要果斷停損 避
Thumbnail
重視籌碼分析 基本面跟技術面需要去研究線圖指標或財報等財務資訊 但籌碼只要花點時間研究或許就能看出端倪 分點解析搭配技術型態也常能搭上主力的順風車 跟著勝利券商操作 勝率也能大大提升! 改變一下分享的個股 即便如此 停損停利的重要性還是要有 萬一不如預期 一定要果斷停損 避
Thumbnail
保持理性面對市場波動,避免過度對沖和降低槓桿,監控日元匯率,並保持長期投資視角。了解市場調整期的應對策略、事件型交易的操作技巧,以及行業領頭羊的動向。關注經濟數據和市場傳聞,靈活調整投資策略,確保投資決策基於充分的信息和合理的分析。
Thumbnail
保持理性面對市場波動,避免過度對沖和降低槓桿,監控日元匯率,並保持長期投資視角。了解市場調整期的應對策略、事件型交易的操作技巧,以及行業領頭羊的動向。關注經濟數據和市場傳聞,靈活調整投資策略,確保投資決策基於充分的信息和合理的分析。
Thumbnail
重視籌碼分析 基本面跟技術面需要去研究線圖指標或財報等財務資訊 但籌碼只要花點時間研究或許就能看出端倪 過往刻意選了本身股性比較活潑的個股股票 經過思考之後 從今開始會改變一下分享的個股 會開始帶一些用分點進出判斷多空 即便如此 停損停利的重要性還是要有 不如預期 一定要
Thumbnail
重視籌碼分析 基本面跟技術面需要去研究線圖指標或財報等財務資訊 但籌碼只要花點時間研究或許就能看出端倪 過往刻意選了本身股性比較活潑的個股股票 經過思考之後 從今開始會改變一下分享的個股 會開始帶一些用分點進出判斷多空 即便如此 停損停利的重要性還是要有 不如預期 一定要
Thumbnail
需求情境: 一般的看盤軟體,雖然都能針對一籃子自選股票,列出其即時行情和當天漲幅,但若要看「五日漲幅」呢?那就少見了,但這對我很重要。因為小部位的波段性價差交易是個好策略,這時候若能排序好一整排看下來,可以節省大量點來點去的成本,很有價值,所以就來自己刻。 解決方案: 從大處著眼,UI 最外層
Thumbnail
需求情境: 一般的看盤軟體,雖然都能針對一籃子自選股票,列出其即時行情和當天漲幅,但若要看「五日漲幅」呢?那就少見了,但這對我很重要。因為小部位的波段性價差交易是個好策略,這時候若能排序好一整排看下來,可以節省大量點來點去的成本,很有價值,所以就來自己刻。 解決方案: 從大處著眼,UI 最外層
Thumbnail
重視籌碼分析 基本面跟技術面需要去研究線圖指標或財報等財務資訊 但籌碼只要花點時間研究或許就能看出端倪 改變一下分享的個股 即便如此 停損停利的重要性還是要有 萬一不如預期 一定要果斷停損 避免讓傷害擴大
Thumbnail
重視籌碼分析 基本面跟技術面需要去研究線圖指標或財報等財務資訊 但籌碼只要花點時間研究或許就能看出端倪 改變一下分享的個股 即便如此 停損停利的重要性還是要有 萬一不如預期 一定要果斷停損 避免讓傷害擴大
Thumbnail
介紹了股票投資時常用的市場面指標、基本面指標、技術面指標、籌碼面指標、財務面指標等。並聚焦於淨值、總市值和股本的相關補充內容。
Thumbnail
介紹了股票投資時常用的市場面指標、基本面指標、技術面指標、籌碼面指標、財務面指標等。並聚焦於淨值、總市值和股本的相關補充內容。
Thumbnail
本文章內容涉及股市指標包括市場面、基本面、技術面、籌碼面和財務面指標,以及股價、漲跌幅度、成交量、主力買賣超、貝塔值等相關內容。這些指標對於選股時常用的多個維度提供了綜合評價,投資者可根據自己的策略採用不同組合的指標進行分析。
Thumbnail
本文章內容涉及股市指標包括市場面、基本面、技術面、籌碼面和財務面指標,以及股價、漲跌幅度、成交量、主力買賣超、貝塔值等相關內容。這些指標對於選股時常用的多個維度提供了綜合評價,投資者可根據自己的策略採用不同組合的指標進行分析。
Thumbnail
本文介紹了作者對股債配置的看法,以及使用EXCEL統整的效率方法。並提及近期債市崩跌、高息型商品不再增加等策略。期望讀者能從中獲得市場贏家的啟示。
Thumbnail
本文介紹了作者對股債配置的看法,以及使用EXCEL統整的效率方法。並提及近期債市崩跌、高息型商品不再增加等策略。期望讀者能從中獲得市場贏家的啟示。
Thumbnail
本文章分享了最新的大盤動向和技術指標統計分析,並提供了一些個股的觀察和操作建議。文章強調了看撐不看壓的理念,並詳細解釋瞭如何操作電腦週邊族群中的個股。此外,文章還提到了技術指標統計中的熱門股票,並提供了資金流向和族群性等相關訊息。
Thumbnail
本文章分享了最新的大盤動向和技術指標統計分析,並提供了一些個股的觀察和操作建議。文章強調了看撐不看壓的理念,並詳細解釋瞭如何操作電腦週邊族群中的個股。此外,文章還提到了技術指標統計中的熱門股票,並提供了資金流向和族群性等相關訊息。
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News