Realm Object-Level Notifications inside UICollectionViewCell's

不想你离开。 提交于 2019-12-12 04:35:22

问题


I have a collection view with Album objects. An Albumcould be favorited or unfavorited by tapping a button inside of the cell. The buttons image changes depending on if the Album is favorited or not. This part is easy and works.

The problem I have is:

  • If you select a cell, a new view controller appears with the Album object in the cell. Within this view controller, you can favorite or unfavorite the Album object. Now when I dismiss this view controller, the button in the cell is "not" updated based on Album's isFavorite property.

I think the solution is to use Realm's Object-Level Notifications inside the UICollectionViewCell's. So that when you favorite/unfavorite an Album in different view controllers, when you come back to the collection view, the button is up to date. But I have no idea how to add and remove the notifications - like where to add/remove and where to update based on the notification?

Note: Please do not say to use collectionView.reloadData().

This is what I have so far (notice the comment: var notificationToken: NotificationToken? // Is this where I add the notification for Realm?):

class Album: Object {
    dynamic var title = ""
    dynamic var isFavorite = false

    convenience init(json: [String: Any]) throws {
        self.init()

        guard let title = json["title"] as? String else {
            throw SerializationError.invalidJSON("Album")
        }

        self.title = title
    }
}

protocol AlbumCollectionViewCellDelegate: class {
    func didTapFavoriteButton(_ favoriteButton: UIButton, album: Album)
}

class AlbumCollectionViewCell: UICollectionViewCell {
    @IBOutlet weak var titleLabel: UILabel!
    @IBOutlet weak var favoriteButton: UIButton!

    weak var delegate: AlbumCollectionViewCellDelegate?
    var album: Album!

    // HELP
    var notificationToken: NotificationToken? // Is this where I add the notification for Realm?

    @IBAction func didTapFavoriteButton(_ sender: UIButton) {
        delegate?.didTapFavoriteButton(sender, album: album)
    }

    func configure(with album: Album) {
        titleLabel.text = album.title
        favoriteButton.isSelected = album.isFavorite
        self.album = album
    }
}

class FavoritesListViewController: UIViewController, AlbumCollectionViewCellDelegate {

    // MARK: - AlbumCollectionViewCellDelegate
    func didTapFavoriteButton(_ favoriteButton: UIButton, album: Album) {
        favoriteButton.isSelected = album.isFavorite
    }
}

Any thoughts?


回答1:


I know you said not to mention reloadData(), but if the view controller was guaranteed to be off screen and would be called back onto the screen, I would instead just call reloadData() (or even just reload​Items(at:​ [Index​Path]) passing in the index paths of the visible items) into the viewWillAppear(animated:) method of the view controller.

It would be much easier than managing sets of notification tokens, especially if you're not even using notification tokens when the view controller is on screen to begin with. :)

That being said, if you want to avoid manually updating the view, when it's onscreen and offscreen and rely on notifications all the time, then yes, Realm's object-level notifications would be the most appropriate feature to use, and yes, adding the logic to the collection view cell subclass would be the most appropriate. :)

class AlbumCollectionViewCell: UICollectionViewCell {
    @IBOutlet weak var titleLabel: UILabel!
    @IBOutlet weak var favoriteButton: UIButton!

    weak var delegate: AlbumCollectionViewCellDelegate?
    var album: Album!

    var notificationToken: NotificationToken? 

    @IBAction func didTapFavoriteButton(_ sender: UIButton) {
        delegate?.didTapFavoriteButton(sender, album: album)
    }

    override func prepareForReuse() {
        notificationToken.stop()
        notificationToken = nil
    }

    func configure(with album: Album) {
        titleLabel.text = album.title
        favoriteButton.isSelected = album.isFavorite
        self.album = album

        notificationToken = self.album.addNotificationBlock { change in
            switch change {
            case .change(let properties):
                for property in properties {
                    if property.name == "isFavorite" {
                        self.favoriteButton.isSelected = self.album.isFavorite
                    }
                }
            }
        }
    }
}



回答2:


I know that you don't want to be told to use reloadData, but have you considered reloadItems(at:[IndexPath])? That would only load the cell that you clicked on in the first place ... Depending on your situation, that might be a simpler approach.

That said, I would think that you add the notification block when the cell's Album value changes. Something like:

var album: Album! {
    didSet {
        self.notificationToken = album.addNotificationBlock { change in
           // The block code
        } 
    }
}


来源:https://stackoverflow.com/questions/42988499/realm-object-level-notifications-inside-uicollectionviewcells

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!