UICollectionViewCell Animation With Protocol - ios

Im trying to animate my menu which made with UICollectionView. I have 2 more collection view which you can see in the below.
And I'm trying to animate top bar when scrolled down. When animation is completed the "Pokemon" label will be white, the pokemon image will be hidden, and the background will be blue. I use protocol to reach cell. This is my ViewController class.
protocol TopCategoriesDelegator{
func openTopBar()
func closeTopBar()}
And this is cellForRowAt function
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if collectionView == self.collectionView{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "topCategory", for: indexPath) as! TopCategoriesCell
cell.viewController = self
return cell
}else if collectionView == subCategoriesCollectionView{
return collectionView.dequeueReusableCell(withReuseIdentifier: "subCategories", for: indexPath)
}else{
return collectionView.dequeueReusableCell(withReuseIdentifier: "productCell", for: indexPath)
}
}
And for detection of scrolling down;
func scrollViewDidScroll(_ scrollView: UIScrollView) {
// print(scrollView.contentOffset.y)
if scrollView.contentOffset.y > 160 {
if self.cellDelegate != nil{
self.cellDelegate.closeTopBar() // func in main vc for background color and size
closeAnimation()
}
}else{
if self.cellDelegate != nil{
self.cellDelegate.openTopBar() // func in main vc for background color and size
openAnimation()
}
}
}
And this is the TopCategoriesCell class
override func layoutSubviews() {
if let vc = viewController as? ProductsVC{
vc.cellDelegate = self
}
}
func closeTopBar() {
UIView.animate(withDuration: 0.3) {
self.categoryImage.frame = CGRect(x: 0, y: 0, width: 0, height: 0)
self.categoryImage.isHidden = true
}
}
func openTopBar(){
UIView.transition(with: categoryImage, duration: 0.3, options: .curveEaseIn, animations: {
self.categoryImage.isHidden = false
self.categoryName.textColor = UIColor().rgb(red: 37.0, green: 110.0, blue: 140.0)
}, completion: nil)
}
Actually everything works fine. But only first element dissappear the others stays there like this;
How can i hide other cell's images ?
Thank You.

You have set the cell delegate of the view controller to be the cell. This seems unnecessary.
It also means that as there is only one view controller, and delegates are a one-to-one communication pattern, your view controller only hides the image on one cell.
To fix this use NotificationCenter in the TopCategoriesCell initialiser as follows:
init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
NotificationCenter.default.addObserver(self, selector: #selector(TopCategoriesCell.closeTopBar), name: NSNotification.Name(rawValue: "scrolledDown"), object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(TopCategoriesCell.openTopBar), name: NSNotification.Name(rawValue: "scrolledUp"), object: nil)
}
Note that which init function you place it in depends on how you are instantiating the cell.
Then, in your view controller:
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if scrollView.contentOffset.y > 160 {
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "scrolledDown"), object: nil)
} else {
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "scrolledUp"), object: nil)
openAnimation()
}
}
And in the TopCategoriesCell:
deinit {
NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: "scrolledDown"), object: nil)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: "scrolledUp"), object: nil)
}

Related

How to bring out the viewController after menu item is selected?

Im trying to bring out the viewcontroller after i select an item in the menu. Currently, when I press something it just change the page, but it gets stuck on the right side. How do i bring that out.
This is the code that i wrote.
class ContainerVC: UIViewController {
var sideMenuOpen = false
#IBOutlet weak var mainView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self,
selector: #selector(toggleSideMenu),
name: NSNotification.Name("ToggleSideMenu"),
object: nil)
}
#IBOutlet weak var sideMenuConstraint: NSLayoutConstraint!
#objc func toggleSideMenu() {
if sideMenuOpen {
sideMenuOpen = false
sideMenuConstraint.constant = -270
} else {
sideMenuOpen = true
sideMenuConstraint.constant = 0
}
UIView.animate(withDuration: 0.3) {
self.view.layoutIfNeeded()
}
}
}
And this is what i wrote in my SideMenuVC
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print(indexPath.row)
// NotificationCenter.default.post(name: NSNotification.Name ("ToggleSideMenu"), object: nil)
switch indexPath.row {
case 0: NotificationCenter.default.post(name: NSNotification.Name ("ShowProfile"), object: nil)
case 8: //signs out of Firebase
try? Auth.auth().signOut()
//signs out of Facebook
FBSDKAccessToken.setCurrent(nil)
let main = UIStoryboard(name: "Main", bundle: nil)
let second = main.instantiateViewController(withIdentifier: "loginPage")
self.present(second, animated: true, completion: nil)
default: break
//https://www.youtube.com/watch?v=Hl_Re_KLhcY
}
}
And this is what i wrote in my HomeVC
#IBAction func menuButton() {
print("Toggle Side Menu")
NotificationCenter.default.post(name: NSNotification.Name("ToggleSideMenu"), object: nil)
}
i would like the selected page to come out.
Just add NotificationCenter.default.post(name: NSNotification.Name ("ToggleSideMenu"), object: nil) to your didSelectRowAt

Scroll UITableView to certain indexPath depending on selection

I have a UITableView with a custom cell. The custom cell as three buttons covering the entire span of the cell. I am trying to scroll the tableview automatically depending on which label the user selects.
I tried using NotificationCenter but it crashes when trying to scroll. Is there a more elegant solution?
Edit: My end goal is to have the user click on optionOne and have it scroll to the cell immediately below. The trick is doing this without cellForRowAt because I'm handling it in the cell due to the multiple buttons.
Custom Cell:
#IBOutlet weak var optionOneLabel: UILabel!
#IBOutlet weak var optionTwoLabel: UILabel!
#IBOutlet weak var optionThreeLabel: UILabel!
override func prepareForReuse() {
super.prepareForReuse()
let optionOne = UITapGestureRecognizer(target: self, action: #selector(CustomCell.optionOnePressed(_:)))
optionOneLabel.addGestureRecognizer(optionOne)
let optionTwo = UITapGestureRecognizer(target: self, action: #selector(CustomCell.optionTwoPressed(_:)))
optionTwoLabel.addGestureRecognizer(optionTwo)
let optionThree = UITapGestureRecognizer(target: self, action: #selector(CustomCell.optionThreePressed(_:)))
optionThreeLabel.addGestureRecognizer(optionThree)
}
#objc func optionOnePressed(_ sender: UITapGestureRecognizer) {
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "scrollTableOptionOne"), object: nil)
}
#objc func optionTwoPressed(_ sender: UITapGestureRecognizer) {
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "scrollTableOptionTwo"), object: nil)
}
#objc func optionThreePressed(_ sender: UITapGestureRecognizer) {
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "scrollTableOptionthree"), object: nil)
}
TableViewController
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(optionOne), name: NSNotification.Name(rawValue: "scrollTableOptionOne"), object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(optionTwo), name: NSNotification.Name(rawValue: "scrollTableOptionTwo"), object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(optionThree), name: NSNotification.Name(rawValue: "scrollTableOptionThree"), object: nil)
}
#objc func optionOne() {
let indexPath = NSIndexPath(item: posts.count, section: 0)
tableView.scrollToRow(at: indexPath as IndexPath, at: UITableViewScrollPosition.init(rawValue: indexPath.row + 2)!, animated: true)
}
#objc func optionTwo() {
print("Don't scroll this one for now")
}
#objc func optionThree() {
print("Don't scroll this one for now")
}
Let me know if you need more information. Thanks.
Rather than use notifications, you could set up a callback on the cell with the buttons so that when one of them is tapped, the callback is run with the information about which button was tapped so that the view controller can do what it needs to with it:
As an example of what you want to do I've created a sample project which you can download and look at (Xcode 9.4.1, Swift 4.1) It's a bit messy, but it does what you need: https://bitbucket.org/abizern/so51758928/src/master/
The specifics are, firstly, set up the cell with the buttons to handle the callback:
class SelectionCell: UITableViewCell {
enum Button {
case first
case second
case third
}
var callback: ((Button) -> Void)?
#IBAction func firstButtonSelected(_ sender: UIButton) {
callback?(.first)
}
#IBAction func secondButtonSelected(_ sender: UIButton) {
callback?(.second)
}
#IBAction func thirdButtonSelected(_ sender: UIButton) {
callback?(.third)
}
}
Using an enum for the buttons makes the code cleaner than just raw numbers to identify a button. Each button calls the callback identifying itself.
In the table view controller, when you set up the cells in the delegate, you pass this callback to the cell:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let value = values[indexPath.row]
switch value {
case .header:
let cell = tableView.dequeueReusableCell(withIdentifier: "Selection", for: indexPath) as! SelectionCell
cell.callback = handler
return cell
case .value:
let cell = tableView.dequeueReusableCell(withIdentifier: "Display", for: indexPath) as! DisplayCell
cell.configure(with: value)
return cell
}
}
Note, I've passed handler not handler() so that it passed the closure, not the result of running the closure, to the cell. It's the cell that runs the closure.
The callback looks like this (for my example, your needs will be different)
I'm using the button identifier that is passed in to the closure to identify the IndexPath that I'm interested in and scrolling it to the top
private func handler(button: SelectionCell.Button) -> Void {
print("selected \(button)")
let index: Int?
switch button {
case .first:
index = values.index(where: { (value) -> Bool in
if case .value("First") = value {
return true
}
return false
})
case .second:
index = values.index(where: { (value) -> Bool in
if case .value("Second") = value {
return true
}
return false
})
case .third:
index = values.index(where: { (value) -> Bool in
if case .value("Third") = value {
return true
}
return false
})
}
guard let row = index else { return }
let indexPath = IndexPath(row: row, section: 0)
tableView.scrollToRow(at: indexPath, at: .top, animated: true)
}
As I've already said, you can look at the example project to see how this all works, but these are the important steps.
Use the cell that sends the notification as the sender object instead of nil. In your cell do:
NotificationCenter.default.post(
name: NSNotification.Name(rawValue: "scrollTableOptionOne"),
object: self)
Change your notification handler so that it can access to the notification. Retrieve the cell from the notification and find out the indexPath.
#objc func optionOne(ntf: Notification) {
let cell = ntf.object as! UITableViewCell
if let indexPath = tableView.indexPath(for: cell) {
tableView.scrollToRow(at: indexPath,
at: .bottom, animated: true)
}
}
I think this is the reason for crashing.
For performance reasons, you should only reset attributes of the cell
that are not related to content, for example, alpha, editing, and
selection state. The table view's delegate in
tableView(_:cellForRowAt:) should always reset all content when
reusing a cell
Refer this
You can make use of Closures or Delegate instead of going with notification
I think you need to change code like this
let indexPath = IndexPath(row: posts.count - 1, section: 0)
tableView.scrollToRow(at: indexPath, at: .top, animated: true)
This will scroll your row to top in table view.
ScrollToRow method having some problem. UITableViewScrollPosition holds the values - none, top, middle, bottom enum values.
Change the line:
tableView.scrollToRow(at: indexPath as IndexPath, at: UITableViewScrollPosition.init(rawValue: indexPath.row + 2)!, animated: true)
To:
tableView.scrollToRow(at: indexPath as IndexPath, at: .top, animated: true)
In order to track indexpath, set row value to cell's tag, in cellForRow:
cell.tag = indexPath.row
And post notification as:
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "scrollTableOptionOne"), object: self.tag)
#objc func optionOne(_ notification: Notification) {
let indexPath = NSIndexPath(item: notification.object+1, section: 0)
tableView.scrollToRow(at: indexPath as IndexPath, at: UITableViewScrollPosition.init(rawValue: indexPath.row + 2)!, animated: true)
}
Better approach will be using closures:
#IBOutlet weak var optionOneLabel: UILabel!
#IBOutlet weak var optionTwoLabel: UILabel!
#IBOutlet weak var optionThreeLabel: UILabel!
var callBackOnLabel : (()->())? /// Add this in cell.
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
let optionOne = UITapGestureRecognizer(target: self, action: #selector(CustomCell.optionOnePressed(_:)))
optionOneLabel.addGestureRecognizer(optionOne)
}
#objc func optionOnePressed(_ sender: UITapGestureRecognizer) {
self.callBackOnLabel?()
}
Then in your cellForRowAt:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
cell.callBackOnLabel = {
let selectedIndex = IndexPath(row: posts.count, section: 0)
tableView.scrollToRow(at: selectedIndex, at: .middle, animated: true)
}
}
In the same way you can add for other two labels or you can make it generic to accept the response in single call back and perform the animation.

Function not triggering on scroll

I am trying to trigger a certain function in a collection view when I scroll. Basically Im trying to implement pagination so that when it scrolls to the bottom more items are loaded.
override func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
if indexPath.item >= allEvents.count - 1 {
// print("paginating for post")
paginationHelper.paginate(completion: { [unowned self] (events) in
self.allEvents.append(contentsOf: events)
DispatchQueue.main.async {
self.collectionView?.reloadData()
}
})
}else{
print("Not paginating")
}
}
This is the entire Function
import Foundation
import UIKit
import Alamofire
import AlamofireNetworkActivityIndicator
import SwiftLocation
import CoreLocation
import AMScrollingNavbar
class HomeFeedController: UICollectionViewController, UICollectionViewDelegateFlowLayout,UIGestureRecognizerDelegate {
// let dropDownLauncher = DropDownLauncher()
var isFinishedPaging = false
let detailView = EventDetailViewController()
let refreshControl = UIRefreshControl()
var emptyLabel: UILabel?
var allEvents = [Event]()
//will containt array of event keys
var eventKeys = [String]()
let customCellIdentifier = "customCellIdentifier"
var grideLayout = GridLayout(numberOfColumns: 2)
lazy var dropDownLauncer : DropDownLauncher = {
let launcer = DropDownLauncher()
launcer.homeFeed = self
return launcer
}()
let paginationHelper = PaginationHelper<Event>(serviceMethod: PostService.showEvent)
override func viewDidLoad() {
super.viewDidLoad()
collectionView?.backgroundColor = UIColor.white
collectionView?.collectionViewLayout = grideLayout
collectionView?.reloadData()
self.collectionView?.contentInset = UIEdgeInsetsMake(15, 0, 0, 0)
navigationItem.title = "Home"
collectionView?.register(CustomCell.self, forCellWithReuseIdentifier: customCellIdentifier)
// self.navigationItem.hidesBackButton = true
let backButton = UIBarButtonItem(image: UIImage(named: "menu"), style: .plain, target: self, action: #selector(handleDropDownMenu))
self.navigationItem.leftBarButtonItem = backButton
// PostService.showEvent(location: User.current.location!) { (event) in
// self.allEvents = event
// print(self.allEvents)
//
// DispatchQueue.main.async {
// self.collectionView?.reloadData()
// }
// }
configureCollectionView()
reloadHomeFeed()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if let navigationController = self.navigationController as? ScrollingNavigationController {
navigationController.followScrollView(self.collectionView!, delay: 50.0)
}
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
if let navigationController = navigationController as? ScrollingNavigationController {
navigationController.stopFollowingScrollView()
}
}
override func scrollViewShouldScrollToTop(_ scrollView: UIScrollView) -> Bool {
if let navigationController = navigationController as? ScrollingNavigationController {
navigationController.showNavbar(animated: true)
}
return true
}
func handleDropDownMenu(){
dropDownLauncer.showDropDown()
}
//will query by selected category
func categoryFetch(dropDown: DropDown){
navigationItem.title = dropDown.name
paginationHelper.category = dropDown.name
configureCollectionView()
reloadHomeFeed()
}
func reloadHomeFeed() {
self.paginationHelper.reloadData(completion: { [unowned self] (events) in
self.allEvents = events
if self.refreshControl.isRefreshing {
self.refreshControl.endRefreshing()
}
DispatchQueue.main.async {
self.collectionView?.reloadData()
}
})
}
func configureCollectionView() {
// add pull to refresh
refreshControl.addTarget(self, action: #selector(reloadHomeFeed), for: .valueChanged)
collectionView?.addSubview(refreshControl)
}
// need to tell it how many cells to have
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return allEvents.count
}
// need to tell the collection view controller what type of cell we want to return
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let customCell = collectionView.dequeueReusableCell(withReuseIdentifier: customCellIdentifier, for: indexPath) as! CustomCell
let imageURL = URL(string: allEvents[indexPath.item].currentEventImage)
print(imageURL ?? "")
customCell.sampleImage.af_setImage(withURL: imageURL!)
return customCell
}
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
//let selectedEvent = self.imageArray[indexPath.row]
//let eventDetailVC
if let cell = collectionView.cellForItem(at: indexPath){
// print("Look here for event name")
// print(detailView.eventName)
detailView.eventKey = allEvents[indexPath.row].key!
detailView.eventPromo = allEvents[indexPath.row].currentEventPromo!
detailView.currentEvent = allEvents[indexPath.row]
present(detailView, animated: true, completion: nil)
//self.navigationController?.pushViewController(detailView, animated: true)
}
print("Cell \(indexPath.row) selected")
}
override func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
if indexPath.item >= allEvents.count - 1 {
// print("paginating for post")
paginationHelper.paginate(completion: { [unowned self] (events) in
self.allEvents.append(contentsOf: events)
DispatchQueue.main.async {
self.collectionView?.reloadData()
}
})
}else{
print("Not paginating")
}
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
//will make surepictures keep same orientation even if you flip screen
// will most likely lock into portrait mode but still good to have
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
grideLayout.invalidateLayout()
}
//Will allow the first two cells that are displayed to be of varying widths
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
if indexPath.item == 0 || indexPath.item == 1 {
return CGSize(width: view.frame.width, height: grideLayout.itemSize.height)
}else{
return grideLayout.itemSize
}
}
func showLeftView(sender: AnyObject?){
print("Button Pressed")
// sideMenuController?.leftViewController = LeftViewController()
//sideMenuController?.showLeftView(animated: true, completionHandler: nil)
}
}
//responsible for populating each cell with content
class CustomCell: UICollectionViewCell {
override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
}
let sampleImage: UIImageView = {
let firstImage = UIImageView()
firstImage.clipsToBounds = true
firstImage.translatesAutoresizingMaskIntoConstraints = false
firstImage.contentMode = .scaleToFill
firstImage.layer.masksToBounds = true
return firstImage
}()
let nameLabel: UILabel = {
let name = UILabel()
name.text = "Custom Text"
name.translatesAutoresizingMaskIntoConstraints = false
return name
}()
func setupViews() {
addSubview(sampleImage)
backgroundColor = UIColor.white
//addSubview(nameLabel)
addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[v0]|", options: NSLayoutFormatOptions(), metrics: nil, views: ["v0": sampleImage]))
addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[v0]|", options: NSLayoutFormatOptions(), metrics: nil, views: ["v0": sampleImage]))
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
// responsible for creating the grid layout that you see in the home view feed
class GridLayout: UICollectionViewFlowLayout {
var numberOfColumns:Int = 2
init(numberOfColumns: Int) {
super.init()
// controlls spacing inbetween them as well as spacing below them to next item
self.numberOfColumns = numberOfColumns
self.minimumInteritemSpacing = 3
self.minimumLineSpacing = 5
}
// just needs to be here because swift tells us to
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override var itemSize: CGSize{
get{
if collectionView != nil {
let collectionVieweWidth = collectionView?.frame.width
let itemWidth = (collectionVieweWidth!/CGFloat(self.numberOfColumns)) - self.minimumInteritemSpacing
let itemHeight: CGFloat = 200
return CGSize(width: itemWidth, height: itemHeight)
}
return CGSize(width: 100, height: 100)
}set{
super.itemSize = newValue
}
}
}
This is the function that is responsible for triggering the event but it seems to go to the else statement literally every time. Any insight would be helpful

Send data between two cells from different collection views embedded in the same viewController

I have two collectionViews embedded in the same viewController and inside each collectionView I added a UISlider that control the volume of an AVAudioplayer. What I need is that when I change the value of one of the two sliders the value of the other change, the two sliders control the same AVAudioPlayer.
Edit:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
switch collectionView {
case mainCollectionView:
let cell = mainCollectionView.dequeueReusableCell(withReuseIdentifier: "mainCell", for: indexPath) as! MainCell
if cell.isSelected {
cell.backgroundColor = UIColor.magenta
cell.audioPlayer = indexOfAudioPlayer[indexPath]
cell.volumeSlider.isHidden = false
} else {
cell.backgroundColor = colorArray[indexPath.section]
cell.volumeSlider.isHidden = true
}
return cell
case soundVolumeCollectionView:
let cell = soundVolumeCollectionView.dequeueReusableCell(withReuseIdentifier: "soundVolumeCell", for: indexPath) as! MainCell
cell.audioPlayer = sortedAudioPlayers[indexPath.item]
return cell
default:
let cell = menuCollectionView.dequeueReusableCell(withReuseIdentifier: "menuCell", for: indexPath) as! MenuCell
cell.backgroundColor = colorArray[indexPath.item]
return cell
}
}
Both mainCollectionView and soundVolumeCollectionView are responsible for controlling the AVAudioPlayers.
You should use NotificationCenter. This isn't meant to be final code, but it should get you in the right direction. Your MenuCell class will need to implement similar observers and send similar notifications.
class ViewController: UIViewController, UICollectionViewDataSource {
var volume: Float {
didSet {
player.volume = volume
NotificationCenter.default.post(name: Notification.Name.playerVolumeChanged, object: self, userInfo: ["volume":volume]);
}
}
var player: AVAudioPlayer
override init() {
super.init()
NotificationCenter.default.addObserver(self, selector: #selector(volumeChanged(_:)), name: Notification.Name.sliderChangedVolume, object: nil)
}
func volumeChanged(_ notification:Notification) {
if let userInfo = notification.userInfo, let newVolume = userInfo["volume"] as? Float {
self.volume = newVolume
}
}
}
extension Notification.Name {
static let playerVolumeChanged = Notification.Name("reverse.domain.name.playerVolumeChanged")
static let sliderChangedVolume = Notification.Name("reverse.domain.name.sliderChangedVolume")
}
class MainCell: UICollectionViewCell {
var slider = UISlider()
required init(frame: CGRect) {
super.init(frame: frame)
NotificationCenter.default.addObserver(self, selector: #selector(volumeDidChange(_:)), name: Notification.Name.playerVolumeChanged, object: nil)
slider.addTarget(self, action: #selector(sliderChanged(_:)), for: UIControlEvents.valueChanged);
}
func volumeDidChange(_ notification:Notification) {
if let userInfo = notification.userInfo, let newVolume = userInfo["volume"] as? Float {
self.slider.value = newVolume
}
}
func sliderChanged(_ slider:UISlider) {
NotificationCenter.default.post(name: Notification.Name.sliderChangedVolume, object: self, userInfo: ["volume": slider.value])
}
override func prepareForReuse() {
super.prepareForReuse()
NotificationCenter.default.removeObserver(self, name: Notification.Name.playerVolumeChanged, object: nil)
}
}

Reload collection view data from another view class

I have two containers in a view. The top one has a collection view.
I want to update my collection view from a button when a button is hit from the below container. My button is also changing the value of an array, which my collection view uses.
I thought didSet would do the job but unfortunately did not work.
Top:
class TopViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
#IBOutlet weak var favoritesCV: UICollectionView!
var myFavorites = [] {
didSet {
self.favoritesCV.reloadData()
}
}
override func viewDidAppear(animated: Bool) {
myFavorites = favoritesInstance.favoritesArray
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return myFavorites.count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell : FavoritesCollectionViewCell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath) as! FavoritesCollectionViewCell
var myPath = myFavorites[indexPath.row] as! String
cell.myImage.image = UIImage(named: myPath)
return cell
}
}
Bottom:
class BottomViewController: UIViewController, UIScrollViewDelegate {
#IBAction func addFavorites(sender: AnyObject) {
favoritesInstance.favoritesArray.append("aaaa.jpg")
}
}
Storage Class:
class Favorites {
var favoritesArray:Array <AnyObject>
init(favoritesArray:Array <AnyObject>) {
self.favoritesArray = favoritesArray
}
}
var favoritesInstance = Favorites(favoritesArray:[])
I have added
NSNotificationCenter.defaultCenter().addObserver(self, selector: "loadList:", name:"load", object: nil)
in my viewdidload of my collection view class.
also added a selector which reloads my data when it is called by the Notification Center
func loadList(notification: NSNotification){
self.favoritesCV.reloadData()
}
and for the other class where the button is pressed:
NSNotificationCenter.defaultCenter().postNotificationName("load", object: nil)
Swift 3:
NotificationCenter.default.addObserver(self, selector: #selector(loadList), name:NSNotification.Name(rawValue: "load"), object: nil)
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "load"), object: nil)
Swift 4:
1st class:
NotificationCenter.default.post(name: NSNotification.Name("load"), object: nil)
Class with collectionView:
in viewDidLoad():
NotificationCenter.default.addObserver(self, selector: #selector(loadList(notification:)), name: NSNotification.Name(rawValue: "load"), object: nil)
and function:
#objc func loadList(notification: NSNotification) {
self.collectionView.reloadData()
}
Great! I was looking for this. In Swift 3 the code is slightly different. In collectionviewcontroller:
NotificationCenter.default.addObserver(self, selector: #selector(RoundCollectionViewController.load), name:NSNotification.Name(rawValue: "reload"), object: nil)
and in the other one:
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "load"), object: nil)

Resources