【问题标题】:Realm - Removing and adding objects from List领域 - 从列表中删除和添加对象
【发布时间】:2017-04-12 20:43:03
【问题描述】:

这是question 的后续行动,因为我可能做错了什么。

我的应用中有一个视频播放器。在名为FavoritesVC 的集合视图中有一个视频列表。如果您点击其中一个单元格,则会出现一个名为PlayerVC 的新视图控制器来播放选定的视频。此外,您可以在这个新的视图控制器PlayerVC 中循环浏览集合视图中的所有视频,因为整个列表都已传递。

这里一切正常。

问题是当我想收藏/取消收藏视频时。根据我上面发布的问题,我决定从答案中使用它:

向 Video 类添加一个 isDeleted 属性。当用户 取消收藏视频,从 FavoriteList.videos,将该属性设置为 true,但将其保留在 Realm 中。 稍后(当应用程序退出或视图控制器 解雇),然后您可以对所有对象进行一般查询 isDeleted 为真,然后删除它们(这解决了无头 问题)。

只有在FavoritesVC 中点击单元格时,我才能将其工作,我将领域List 转换为Swift array 并使用此arrayPlayerVC 供电。如果我不这样做,我从List 中删除一个对象并尝试循环通过List,我会得到一个Index out of range error...

我必须将 List 转换为 Swift array 并将其传递给 PlayerVC 的解决方案有效,但在使用 Realm 时似乎错误。 最佳做法是永远不要进行此转换,但在这种情况下,我不知道如何简单地使用List

这是我的列表代码:

/// Model class that manages the ordering of `Video` objects.
final class FavoriteList: Object {
    // MARK: - Properties

    /// `objectId` is set to a static value so that only
    /// one `FavoriteList` object could be saved into Realm.
    dynamic var objectId = 0

    let videos = List<Video>()

    // MARK: - Realm Meta Information

    override class func primaryKey() -> String? {
        return "objectId"
    }
}

我将 List 转换为数组并像这样创建 PlayerVC:

class FavoritesViewController: UIViewController {

    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {

        if let playerVC = self.storyboard?.instantiateViewController(withIdentifier: "playerVC") as? PlayerViewController {

            // I convert the `List` to a Swift `array` here.
            playerVC.videos = Array(favoritesManager.favoriteList.videos)
            playerVC.currentVideoIndex = indexPath.row
            self.parent?.present(playerVC, animated: true, completion: nil)
        }
    }
}

这是PlayerVC 的sn-p。当FavoritesVC 中的一个单元格被点击时,它会被创建:

class PlayerViewControllerr: UIViewController {
    // Array passed from `FavoritesVC`. I converted this from the `List`.
    var videos = [Video]()

    var currentVideoIndex: Int!
    var currentVideo: Video {
        return videos[currentVideoIndex]
    }

    // HELP
    // Example of how to cycle through videos. This cause crash if I use the `List` instead of Swift `array`.
    func playNextVideo() {
        if (currentVideoIndex + 1) >= videos.count {
            currentVideoIndex = 0
        } else {
            currentVideoIndex = currentVideoIndex + 1
        }

        videoPlaybackManager.video = currentVideo
    }

    // This is where I add and remove videos from the `List`
    @IBAction func didToggleFavoriteButton(_ sender: UIButton) {
        favoritesManager.handleFavoriting(currentVideo, from: location)     { [weak self] error in
            if let error = error {
            self?.present(UIAlertController.handleErrorWithMessage(error.localizedDescription, error: error), animated: true, completion: nil)
            }
        }
    }
}

最后是在List中添加和删除对象的代码:

class FavoritesManager {
    let favoriteList: FavoriteList

    init(favoriteList: FavoriteList) {
        self.favoriteList = favoriteList
    }

    /// Adds or removes a `Video` from the `FavoriteList`.
    private func handleAddingAndRemovingVideoFromFavoriteList(_ video: Video) {
        if isFavorite(video) {
            removeVideoFromFavoriteList(video)
        } else {
            addVideoToFavoriteList(video)
        }
    }

    /// Adds a `Video` to the `FavoriteList`.
    ///
    /// Modifies `Video` `isDeleted` property to false so no garbage collection is needed.
    private func addVideoToFavoriteList(_ video: Video) {
        let realm = try! Realm()
        try! realm.write {
            favoriteList.videos.append(video)
            video.isDeleted = false
        }
    }

    /// Removes a `Video` from the `FavoriteList`.
    ///
    /// Modifies `Video` `isDeleted` property to true so garbage collection is needed.
    /// Does not delete the `Video` from Realm or delete it's files.
    private func removeVideoFromFavoriteList(_ video: Video) {
        let predicate = NSPredicate(format: "objectId = %@", video.objectId)
        guard let index = favoriteList.videos.index(matching: predicate) else { return }

        let realm = try! Realm()
        try! realm.write {
            favoriteList.videos.remove(objectAtIndex: index)
            video.isDeleted = true
        }
    }
}

有什么想法吗?

【问题讨论】:

    标签: ios swift realm realm-list


    【解决方案1】:

    这仍然很难简明地回答,因为我仍然不确定当用户“取消收藏”视频时您希望发生什么。

    为了获得最佳用户体验,我假设取消收藏视频仍会让它在屏幕上播放,但是一旦视图控制器被关闭,或者下一个视频开始播放,视频就会从 @987654321 中完全删除@ 目的。这意味着即使将其从 videos 中删除,Video 对象也需要保留,直到用户明确将其关闭。

    我猜测将 videos 转换为 Swift 数组有效的原因是,即使视频从 videos 中删除,它的引用仍保留在 Swift 数组中,因此 currentVideoIndex 值保持同步.

    应该可以直接使用videos 对象,但是由于您在用户点击“不喜欢”按钮时对其进行了变异,因此您无法可靠地存储“currentIndex”,因为这显然会改变。

    在这种情况下,最好提前确定要播放哪些视频,然后存储对它们的强引用。这样,即使videos 发生变化,我们仍然可以智能地计算出我们在播放列表中的位置以及我们将播放的即将播放和之前的视频。

    播放器视图控制器只需要收藏夹管理器和用户点击的当前视频

    class FavoritesViewController: UIViewController {
    
        func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    
            if let playerVC = self.storyboard?.instantiateViewController(withIdentifier: "playerVC") as? PlayerViewController {
                playerVC.favoritesManager = favoritesManager
                playerVC.currentVideo = favoritesManager.favoritesList.videos[indexPath.row]
                self.parent?.present(playerVC, animated: true, completion: nil)
            }
        }
    }
    

    播放器会从逻辑上计算出要排队的视频,即使它们稍后会从videos 列表中删除:

    class PlayerViewControllerr: UIViewController {
        var favoritesManager: FavoriteManager
    
        var currentVideo: Video? {
            didSet { setUpVideos() }
        }
        var nextVideo: Video?
        var previousVideo: Video?
    
        func playNextVideo() {
            currentVideo = nextVideo
            videoPlaybackManager.video = currentVideo
        }
    
        func playPreviousVideo() {
            currentVideo = previousVideo
            videoPlaybackManager.video = currentVideo
        }
    
        func setUpVideos() {
            let videos = favoritesManager.favoritesList.videos
            let index = videos.index(of: currentVideo)
    
            var previousIndex = index - 1
            if previousIndex < 0 { previousIndex = videos.count - 1 }
            previousVideo = videos[previousIndex]
    
            var nextIndex = index + 1
            if nextIndex >= videos.count { nextIndex = 0 }
            nextVideo = videos[nextIndex]
        }
    }
    

    这是假设用户“可能”最终从videos 中删除currentVideo,但用户不能删除nextVideopreviousVideo,直到它们也变成currentVideo。无论如何,既然视图控制器本身强烈引用这些视频对象,独立于索引,这应该有望使复制videos 到 Swift 数组变得不必要。

    【讨论】:

    • 感谢您的回答!!这修复了崩溃!然而,体验与 Swift 数组不同(除非我做错了什么)。现在发生了什么:假设PlayerVC 有 2 个视频。如果您不喜欢其中一个,则会从favoriteList 中删除-> 但现在您在执行playNextVideo 之后无法在PlayerVC 中循环回到此,因为它不再在List 中。 Swift array 的用户体验是:即使视频不受欢迎,您仍然可以循环并再次查看它,即使它不在favoriteList 而在PlayerVC
    • 对不起,如果我的问题的解释有些有限。但最终,如果 Swift array 是一个不错的选择,我也可以使用它吗?
    • 别担心!是的,那是故意的。就像我上面写的那样,我假设一旦它不受欢迎,一旦用户导航离开,它就会完全删除自己。啊,所以您实际上希望视频保留在视频播放器的播放列表中,直到他们将其关闭?好的。是的,制作videos 对象的静态副本很适合。
    • 是的,有时我会删减细节,因为如果不是这样,没有人会阅读长问题,但是解释起来很痛苦,再次道歉。好吧,我将使用 Swift array。我仍然不太了解内存,所以说实话我不确定 Realm 是如何与 Swift 混合的,因为 List 是一个引用类型(我认为),而 Swift array 是一个 struct (值类型)。无论如何,我会接受你的建议。你是一个传奇,总是很棒的答案!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多