问题
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
Albumobject in the cell. Within this view controller, you can favorite or unfavorite theAlbumobject. Now when I dismiss this view controller, the button in the cell is "not" updated based onAlbum'sisFavoriteproperty.
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 reloadItems(at: [IndexPath]) 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