RealmSwift加载时排序属性列表及重排实时更新的实现问题
问题解决方案
一、解决排序报错:"This method may only be called on RLMArray instances retrieved from an RLMRealm"
报错原因
你直接对RealmSwift.List<Game>调用了原地排序方法(比如sort(by:)),但Realm的List是和数据库绑定的持久化集合,不允许直接修改原集合的排序逻辑,必须通过生成新的排序后结果来展示。
修复方法
- 用
@ObservedResults加载Publisher(同时解决后面的视图刷新问题),并直接指定按position排序:
import SwiftUI import RealmSwift struct ContentView: View { // 自动监听Realm中所有Publisher,按position字段排序 @ObservedResults(Publisher.self, sortDescriptor: SortDescriptor(keyPath: \Publisher.position)) private var publishers var body: some View { List { ForEach(publishers) { publisher in Section(header: Text(publisher.name)) { // 对当前publisher的games按position排序后展示,不修改原List ForEach(publisher.games.sorted(by: { $0.position < $1.position })) { game in Text(game.name) } // 编辑模式下的位置交换逻辑 .onMove { indices, newOffset in swapGamePositions(for: publisher, from: indices, to: newOffset) } } } } .toolbar { EditButton() } } }
- 绝对不要尝试对
publisher.games执行原地排序(比如publisher.games.sort(by:)),而是每次展示时用sorted(by:)生成临时的排序数组,这样既不会改动Realm里的原集合,也能正确展示排序后的结果。
二、解决编辑模式下位置交换后视图不自动刷新
问题原因
之前的实现没用到Realm的自动监听机制,或者修改position时没在Realm事务里正确执行,导致视图感知不到数据变化。
修复方法
- 用上面代码里的
@ObservedResults加载Publisher,它会自动订阅Realm的数据变更,一旦数据库内容修改,视图会立刻刷新。 - 位置交换的逻辑必须在Realm事务里修改
Game的position字段,确保变更被Realm捕获:
private func swapGamePositions(for publisher: Publisher, from indices: IndexSet, to newOffset: Int) { guard let realm = publisher.realm else { return } // 先拿到和展示时一致的排序后游戏列表 var sortedGames = publisher.games.sorted(by: { $0.position < $1.position }) // 在临时数组里执行位置交换 sortedGames.move(fromOffsets: indices, toOffset: newOffset) // 在Realm事务里更新每个游戏的position值 try! realm.write { for (index, game) in sortedGames.enumerated() { game.position = index } } }
关键说明
@ObservedResults是Realm为SwiftUI提供的属性包装器,自动监听数据的增删改,不用手动触发视图刷新。- 交换位置时,先在临时数组里调整顺序,再遍历更新每个游戏的
position,确保数据库里的position和展示顺序一致,事务中的修改会被@ObservedResults立刻捕获,触发视图自动刷新。
额外注意事项
- 确保Realm模型定义正确,特别是主键和字段类型:
class Publisher: Object { @Persisted(primaryKey: true) var name: String @Persisted var position: Int @Persisted var games: List<Game> } class Game: Object { @Persisted(primaryKey: true) var name: String @Persisted var description: String @Persisted var cover: String @Persisted var position: Int }
- 别在
onAppear里手动加载排序数据,@ObservedResults已经包含了排序和自动监听功能,重复操作反而容易出错。
内容的提问来源于stack exchange,提问作者Baffo rasta
相关产品推荐
相关产品推荐

