This website records my snippets, patterns, and some practices.
近日,為實作選取語言功能,發現目前 SwiftUI 的 Picker 在記憶體使用方面,有極大的問題存在。
如下,實作一簡易App,只有單一元件用來選取語言,不同風格的 Picker 記憶體耗用如下:
style | init | expanded/scroll | collapsed | comment |
---|---|---|---|---|
automatic | 預設值, 依平台及所在視圖等條件決定呈現方式 | |||
menu | ~114 MB | ~107 MB | ~102 MB | |
inline | ~65 MB | ~53 MB | 配合 Form 使用 | |
navigationLink | ~40 MB | ~45 MB | ~38 MB | 通常配合 NavigationStack 使用 |
palette | ~22 MB | ~139 MB | ~139 MB | 通常配合 Menu 使用 |
segmented | ~63 MB | ~64 MB | ||
wheel | ~43 MB | ~43 MB |
style | init | expanded/scroll | collapsed | comment | |
---|---|---|---|---|---|
automatic | 預設值, 依平台及所在視圖等條件決定呈現方式 | ||||
menu | ~108 MB | ~120 MB | ~120 MB | ||
inline | ~225 MB | ~225 MB | 直接使用時,呈現同 radioGroup, 搭配 Menu 時同 menu | ||
radioGroup | ~221 MB | ~221 MB | |||
palette | ~22 MB | ~139 MB | ~139 MB | 不適合文字資料,每次展開關閉都會增加記憶體 | |
segmented | ~310 MB | ~310 MB | ~310 MB |
struct IOSMenuPicker<SELECTION: Hashable, Content: View>: View {
let title:String
let content: () -> Content
@Binding var selection: SELECTION
@State private var isPicking = false
var body: some View {
Button {
isPicking = true
} label: {
HStack {
if !title.isEmpty {
Text("\(title)")
}
VStack {
Group {
Image(systemName:"control")
Image(systemName: "control").rotationEffect(.degrees(180.0))
}.font(.system(size: 10, weight: .bold))
}
}
}.buttonStyle(.borderless).popover(isPresented: $isPicking) {
ScrollViewReader { scroller in
ScrollView {
LazyVStack {
MenuPickerBody(selection: $selection,
isPicking: $isPicking,
content: content)
}.padding(.vertical, 10)
}.onAppear {
scroller.scrollTo(selection, anchor: .center)
}
}.frame(minWidth: 300).presentationCompactAdaptation(.popover)
}
}
init(_ title: String, selection: Binding<SELECTION>, @ViewBuilder content: @escaping () -> Content) {
self.title = title
self._selection = selection
self.content = content
}
}
在iOS上實測,以上程式碼耗用記憶體初始 < 1 MB,展開後約增加 2~3 MB。 要注意的部分在於,雖然記憶體用量相較Picker及Menu少很多,但每次開閤Menu,還是會有記憶體些微增加的情況。
更進一步的改進,考慮到可供選取的資料較多,應可提供搜尋功能輔助完整列表。但搜尋功能多半與資料特性相關,要設計成泛用元件會有一定難度,在此不另做說明。
最後,附上詳細範例程式。