Back button on UICollectionView - swift

I am trying to add a back/ return button to my UICollectionView, I have this code so far to implement the button:
import UIKit
class EmojiPopup: UIView,UICollectionViewDataSource,UICollectionViewDelegate
{
var collocationView : UICollectionView!
var arrImagesList:NSMutableArray!
var blur:UIBlurEffect = UIBlurEffect()
override init(frame: CGRect)
{
super.init(frame: frame)
arrImagesList = NSMutableArray()
self.backgroundColor = UIColor.purpleColor().colorWithAlphaComponent(0.2)
let layout = UICollectionViewFlowLayout()
//header gap
layout.headerReferenceSize = CGSizeMake(20,20)
//collection view item size
layout.itemSize = CGSizeMake(70, 70)
layout.minimumInteritemSpacing = 25
layout.minimumLineSpacing = 25
collocationView = UICollectionView(frame: CGRectMake(50,50,UIScreen.mainScreen().bounds.screenWidth - 100,UIScreen.mainScreen().bounds.screenHeight - 100), collectionViewLayout: layout)
self.addSubview(collocationView)
// Create the blurEffect and apply to view
let blurEffect = UIBlurEffect(style: UIBlurEffectStyle.ExtraLight)
let blurEffectView = UIVisualEffectView(effect: blurEffect)
blurEffectView.alpha = 0.7
blurEffectView.frame = self.bounds
self.addSubview(blurEffectView)
collocationView.backgroundColor = UIColor.purpleColor().colorWithAlphaComponent(0.002)
collocationView.dataSource = self
collocationView.delegate = self
collocationView.registerClass(UICollectionViewCell.self, forCellWithReuseIdentifier: "cellIdentifier")
//hide scrollbar
self.collocationView.showsVerticalScrollIndicator = false
//back button
let btnBack = UIButton(frame:TCRectMake(x:138 ,y:523,width:45,height:45))
btnBack.setImage(UIImage(named:"back"), forState: UIControlState.Normal)
btnBack.addTarget(self, action:"btnBackClick", forControlEvents: UIControlEvents.TouchUpInside)
self.addSubview(btnBack)
//back button func
func btnBackClick()
{
}
let fm = NSFileManager.defaultManager()
let path = NSBundle.mainBundle().resourcePath!
let items = try! fm.contentsOfDirectoryAtPath(path)
for item in items
{
if item.hasSuffix("png") && item.containsString("#") == false && item.containsString("AppIcon") == false && item.containsString("tick_blue") == false && item.containsString("video_camera") == false
{
arrImagesList.addObject(item)
}
}
}
var completeHandler:((String)->())?
func showDetails(viewParent:UIView,doneButtonClick:((String)->())?)
{
completeHandler = doneButtonClick
viewParent.addSubview(self)
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
{
return arrImagesList.count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell
{
let identifier="ImageCell\(indexPath.section)\(indexPath.row)"
collectionView.registerClass(ImageViewCell.self, forCellWithReuseIdentifier:identifier)
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(identifier, forIndexPath: indexPath) as! ImageViewCell
cell.backgroundColor = UIColor(white:1, alpha:0)
cell.imgView.image = UIImage(named:arrImagesList[indexPath.row] as! String)
cell.imgView.backgroundColor = UIColor.clearColor()
cell.imgView.opaque = false
cell.imgView.contentMode = .ScaleAspectFit
//keeps blur to background
self.bringSubviewToFront(collocationView)
return cell
}
// func collectionView(collectionView: UICollectionView,
// layout collectionViewLayout: UICollectionViewLayout,
// sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize
// {
// let width=UIScreen.mainScreen().bounds.size.width-50
// return CGSize(width:width/3, height:width/3)
// }
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath)
{
//let cell=collectionView.cellForItemAtIndexPath(indexPath) as! ImageViewCell
UIView.animateWithDuration(0.3, animations:{
self.collocationView.alpha=0
}, completion: { finished in
if self.completeHandler != nil
{
self.completeHandler!(self.arrImagesList[indexPath.row] as! String)
}
self.removeFromSuperview()
})
}
func showDetails(viewParent:UIView,dictData : [String:String],index:Int,doneButtonClick:(()->())?,cancelBUttonClick:(()->())?)
{
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
I want the collection view to be closed if the user presses the back button, but I am not sure what to enter in the back button function. I want the user to be returned back to the main view controller (mapview) if possible

I will assume you are talking about a UICollectionViewController and not about a UICollectionView. A UICollectionViewController has a UICollectionView inside. You can "close" (dismiss) a UICollectionViewController but not a UICollectionView. You can even dismiss a UIViewController that has a UICollectionView inside.
You have two options:
Put you collection view controller (and the main view controller) inside a navigation controller so you can use the default back button already implemented by the navigation controller.
You can present the collection view controller modally from the main view controller. Then you need to add a close button (Not back button) that dismiss the controller (The main view controller will stay "behind" so when you dismiss the UICollectionViewController it will become visible again.
It's a long way. I suggest you read this getting started guide from Apple, there you can figure it out how navigation controllers and what they do. This is something you need to learn when developing with Swift. I suggest you go further and read the whole tutorial. After reading that chapter you should understand the navigation flow of an iOS application and implement the back-button navigation.
If you find any trouble following that tutorial, let me know.

Related

page control not working for UICollectionView inside UITableView

Im having issue with making page control work for a collection view which is inside table view. There are actually 2 collection views in the view controller, one is outside the tableView and other one is inside. I want page control for inside one. Let me share code below and then explain what is causing trouble:
class HomeViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
override func viewDidLoad() {
super.viewDidLoad()
//setup stories collection view
let nib = UINib(nibName: "StoryCollectionViewCell", bundle: nil)
cvStories.register(nib, forCellWithReuseIdentifier: "StoryCollectionViewCell")
let layout1 = UICollectionViewFlowLayout()
layout1.scrollDirection = .horizontal
layout1.sectionInset = UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5)
collectionViewStories.setCollectionViewLayout(layout1, animated: true)
collectionViewStories.delegate = self
collectionViewStories.dataSource = self
tblHome.dataSource = self
tblHome.delegate = self
}
// MARK: - Table View
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tblHome.dequeueReusableCell(withIdentifier: "HomeTableViewCell", for: indexPath) as! HomeTableViewCell
cell.collectionViewPostImages.dataSource = self
cell.collectionViewPostImages.delegate = self
cell.collectionViewPostImages.tag = indexPath.row
cell.collectionViewPostImages.reloadData()
cell.pageControl.hidesForSinglePage = true
cell.pageControl.numberOfPages = self.posts[cell.collectionViewPostImages.tag].imagesArray?.count ?? 0
return cell
}
//MARK: - Collection View
func numberOfSections(in collectionView: UICollectionView) -> Int {
if collectionView == cvStories {
return 2
}
else {
return 1
}
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if collectionView == collectionViewStories {
return stories.count
}
else {
return posts[collectionView.tag].imagesArray?.count ?? 0
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if collectionView == collectionViewStories {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "StoryCollectionViewCell", for: indexPath) as! StoryCollectionViewCell
//code to show stories
return cell
}
else {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "HomeCollectionViewCell", for: indexPath) as! HomeCollectionViewCell
//code to show images
return cell
}
}
}
//TableViewCell Class
class HomeTableViewCell: UITableViewCell {
#IBOutlet weak var collectionViewPostImages: UICollectionView!
#IBOutlet weak var pageControl: UIPageControl!
var currentPage = 0
override func awakeFromNib() {
let flowLayout = UICollectionViewFlowLayout()
flowLayout.itemSize = CGSize(width: UIScreen.main.bounds.size.width, height: cvPostImages.frame.size.height)
flowLayout.scrollDirection = .horizontal
flowLayout.minimumInteritemSpacing = 0
flowLayout.minimumLineSpacing = 0
collectionViewPostImages?.collectionViewLayout = flowLayout
collectionViewPostImages.showsHorizontalScrollIndicator = false
collectionViewPostImages.isPagingEnabled = true
}
//ScrollView delegate method
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let pageWidth = scrollView.frame.width
self.currentPage = Int((scrollView.contentOffset.x + pageWidth / 2) / pageWidth)
self.pageControl.currentPage = self.currentPage
}
}
The whole code is like Instagram app, stories at top and posts with images in tableView. Whole code is in single View controller and everything works fine. Even dots (page number) are correct according to number of pages however when you swipe between them they do not change. I saw many solutions and in those pageControl.numberOfPages was used in cellForItemAt function but its out of scope for me so I just called it as cell.pageControl.numberOfPages in cellForRowAt instead. First time dealing with collectionView inside tableView so getting confused getting it worked.
The main problem is you use cell.collectionViewPostImages.delegate = self in HomeViewController but your method scrollViewDidScroll in cell HomeTableViewCell.
Collectionview's own scrollview delegate methods triggering in UICollectionViewDelegate. Thats why scrollViewDidScroll is not triggered in your cell.
Solution is using scrollViewDidScroll in where you delegete the collectionview. It is HomeViewController
By the way, I dont recommend to use cell.collectionViewPostImages.dataSource = self cell.collectionViewPostImages.delegate = self in reusable cell. You dont want to make ...delegate = self on everytime when cell reused. You can take it in awakeFromNib in cell class

UICollectionView numberOfItemsInSection not being called on reloadData() - Swift Xcode

I have two view controllers – SecondTestViewController and SecondContentViewController. SecondContentViewController is a floating panel inside SecondTestViewController. SecondTestViewController has a UITableView and SecondContentViewController has a UICollectionView.
SecondContentViewController:
override func viewDidLoad() {
super.viewDidLoad()
// let layout = UICollectionViewFlowLayout()
// layout.scrollDirection = .vertical
myCollectionView = UICollectionView(frame: .zero, collectionViewLayout: collectionViewLayout())
guard let myCollectionView = myCollectionView else {
return
}
myCollectionView.register(CollectionViewCell.self, forCellWithReuseIdentifier: "CollectionViewCell")
myCollectionView.delegate = self
myCollectionView.dataSource = self
myCollectionView.backgroundColor = .white
myCollectionView.translatesAutoresizingMaskIntoConstraints = false
myCollectionView.allowsSelection = true
myCollectionView.isSpringLoaded = true
view.backgroundColor = .white
view.addSubview(myCollectionView)
NSLayoutConstraint.activate([
myCollectionView.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor),
myCollectionView.leftAnchor.constraint(equalTo: self.view.leftAnchor),
myCollectionView.rightAnchor.constraint(equalTo: self.view.rightAnchor),
myCollectionView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor)
])
}
private func collectionViewLayout() -> UICollectionViewLayout {
let layout = UICollectionViewFlowLayout()
let cellWidthHeightConstant: CGFloat = UIScreen.main.bounds.width * 0.2
layout.sectionInset = UIEdgeInsets(top: 50,
left: 10,
bottom: 0,
right: 10)
layout.scrollDirection = .vertical
layout.minimumInteritemSpacing = 0
layout.itemSize = CGSize(width: cellWidthHeightConstant, height: cellWidthHeightConstant)
return layout
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
// return contentData.count
print("numberOfItemsInSection activated")
print("numberOfItemsInSection = \(reviewDataController?.tableViewReviewData.count ?? 100)")
return reviewDataController?.tableViewReviewData.count ?? 0
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
print("cellForItemAt activated")
let cell = myCollectionView?.dequeueReusableCell(withReuseIdentifier: "CollectionViewCell", for: indexPath) as! CollectionViewCell
// cell.data = self.contentData[indexPath.item]
cell.data = self.reviewDataController.tableViewReviewData[indexPath.item]
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: collectionView.frame.width / 5, height: collectionView.frame.height / 10)
}
func updateTheCollectionView() {
print("updateCollectionView activated")
print("reviewData count = \(reviewDataController.tableViewReviewData.count)")
myCollectionView?.reloadData()
}
When a cell in SecondTestViewController's tableView is tapped, it takes a photo, passes the data, and then calls SecondContentViewController's updateTheCollectionView() function.
This function is called in SecondTestViewController after a photo has been taken and various other tasks completed (like updating the variable, etc.).
func passDataToContentVC() {
let contentVC = SecondContentViewController()
contentVC.reviewDataController = self.reviewDataController
contentVC.updateTheCollectionView()
}
As you can see, reloadData is supposed to be called inside this function. It should now update the view with the new photo. For some reason, it's like reloadData is never called because neither numberOfItemsInSection nor cellForItemsAt are being activated a second time.
What is going wrong? The print statements show that the data is being passed (they show an updated quantity after a photo has been taken). The view simply is not updating with the new data because the two collectionView functions are not being called.
Edit:
Here is the floating panel code.
fpc = FloatingPanelController()
fpc.delegate = self
guard let contentVC = storyboard?.instantiateViewController(identifier: "fpc_secondContent") as? SecondContentViewController else {
return
}
fpc.isRemovalInteractionEnabled = false
fpc.set(contentViewController: contentVC)
fpc.addPanel(toParent: self)
It's essentially inside of viewDidLoad for the SecondTestViewController.
For more information on how it works: Visit this github
As far as the didSelectRowAt function goes, it's too much code to show here. reviewDataController is dependency injection made up of an array of a struct. Essentially all that happens is when a cell is tapped, it pulls up the camera and allows the user to take a photo, then it stores the photo in the array inside of reviewDataController along with some other properties in the struct that the array is made of. The important part is that it's supposed to pass this data to the floating panel which then updates the collectionView showing the photo that was just taken.
Because you are passing data from SecondTestViewController to SecondContentViewController, which is forwards and not backwards, you don't need delegates or anything.
Instead, all you need to do is retain a reference to SecondContentViewController. Try something like this:
class SecondTestViewController {
var secondContentReference: SecondContentViewController? /// keep a reference here
override func viewDidLoad() {
super.viewDidLoad()
let fpc = FloatingPanelController()
fpc.delegate = self
guard let contentVC = storyboard?.instantiateViewController(identifier: "fpc_secondContent") as? SecondContentViewController else {
return
}
fpc.isRemovalInteractionEnabled = false
fpc.set(contentViewController: contentVC)
fpc.addPanel(toParent: self)
self.secondContentReference = contentVC /// assign contentVC to the reference
}
}
secondContentReference keeps a reference to your SecondContentViewController. Now, inside passDataToContentVC, use that instead of making a new instance with let contentVC = SecondContentViewController().
Replace
func passDataToContentVC() {
let contentVC = SecondContentViewController()
contentVC.reviewDataController = self.reviewDataController
contentVC.updateTheCollectionView()
}
... with
func passDataToContentVC() {
secondContentReference?.reviewDataController = self.reviewDataController
secondContentReference?.updateTheCollectionView()
}
It seems that
let contentVC = SecondContentViewController() // this is a new instance
contentVC.reviewDataController = self.reviewDataController
contentVC.updateTheCollectionView()
You create an instance and add it to SecondTestViewController but passDataToContentVC has another new one , hence no updates for the shown one

How to use a UISwitch to add and remove data from an Array of Data

I have a tableview with a UISwitch in each of the cells. What I am trying to do is that whenever the UISwitch is Toggled On, it adds that cell into an array and when the switch is Toggled Off it removes it. Right now it only adds and doesn't remove.
Once this is complete I need the CollectionView that is also within this ViewController to update and visually show the newStringArray Cells based on the number in that array and that also is able to appear and disappear based on the cells that have their UISwitch toggled on.
import UIKit
class NewMoveViewController: UIViewController {
private var stringSource = ["String 1,", "String 2", "String 3"]
var newStringArray = Array<String>()
private let tableView: UITableView = {
let tableView = UITableView()
tableView.rowHeight = 100
return tableView
}()
private var collectionView: UICollectionView?
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(tableView)
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
layout.itemSize = CGSize(width: 50, height: 50)
collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
collectionView?.register(NewMoveCollectionViewCell.self, forCellWithReuseIdentifier: NewMoveCollectionViewCell.identifier)
collectionView?.showsHorizontalScrollIndicator = false
title = "Add to Group"
tableView.register(NewMoveTableViewCell.self, forCellReuseIdentifier: NewMoveTableViewCell.identifier)
tableView.delegate = self
tableView.dataSource = self
collectionView?.backgroundColor = .systemBlue
collectionView?.dataSource = self
collectionView?.delegate = self
guard let myCollection = collectionView else {
return
}
view.addSubview(myCollection)
// Do any additional setup after loading the view.
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
collectionView?.frame = CGRect(x: 0, y: 100, width: view.frame.size.width, height: 50)
tableView.frame = CGRect(x: 0, y: 200, width: view.frame.size.width, height: view.frame.size.height)
}
}
extension NewMoveViewController : UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: NewMoveTableViewCell.identifier, for: indexPath) as! NewMoveTableViewCell
let switchView = UISwitch(frame: .zero)
switchView.setOn(false, animated: true)
switchView.tag = indexPath.row
switchView.addTarget(self, action: #selector(self.switchDidChange(_:)), for: .valueChanged)
cell.accessoryView = switchView
cell.configure(with: "", label: "test")
return cell
}
#objc func switchDidChange(_ sender: UISwitch) {
newStringArray.append(stringSource[sender.tag])
// newStringArray.remove(stringSource.remove(at: [s]))
print(newStringArray)
}
func tableView(_ tableView: UITableView, shouldHighlightRowAt indexPath: IndexPath) -> Bool {
return false
}
}
extension NewMoveViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return newStringArray.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: NewMoveCollectionViewCell.identifier, for: indexPath) as! NewMoveCollectionViewCell
return cell
}
}
the hardest part is to remove an object from an array, my approach on these situations is to transform my array in a NSMutableArray because it have a function to remove an specific object, then make a delegate in the cell that informs the viewController to remove the object from the list and reload the tableView.
the delegate wil be something like this:
protocol RemoveObjectFromCell {
func removeObjectIncell(object: MyObject)
}
class myCell: UITableViewCell {
//your outlets and variables
var object: MyObject?
var delegate: removeObjectIncell?
func setupCell(object: myObject) {
//configure your cell with the specific object
}
}
make sure of calling the delegate on the switch action inside you cell class like this:
#IBAction func switchPressed(sender: UISwitch) {
if !sender.isOn {
self.delegate?.removeObjectIncell(object: self.object)
}
in the view controller implement your protocol and use the required function like this:
class myViewController: UIViewController, RemoveObjectFromCell {
//everything that goes in your class
func removeObjectIncell(object: MyObject) {
self.myArray.remove(object)
DispatchQueue.main.async {
self.myTableView.reloadData()
}
}
}
In order to get changes you want you have to set property which is gonna indicate whether switch is on or off
Something like: var switchIsActive = false
and simply change it in function and if it is turned on you perform one action when it is off you perform another one. Also after function you have to reload your tableView tableView.reloadData()
You can remove elements in your array by their tag by calling Array.remove(at: Int). It can be done by the cells [indexPath.row]

CollectionView cells Confused

I am busy with a speech app with a soundboard when you tap on a CollectionView Cell then iPhone will speak the text. I have a little bug in my app and I don't know what the reason is.
I have built a CollectionView with images as backgroundViews. It works but when I go to an other view ,for example to the Paint view, and I will go back then the cells will confuse.
Can anybody tell me what goes wrong and how I solve it?
Thanks!
This is my code:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
managedObjectContext = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
loadData()
let itemSize = UIScreen.main.bounds.width/2 - 5
let itemHeight = itemSize / 2
let layout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets.init(top: 3, left: 3, bottom: 3, right: 3)
layout.itemSize = CGSize(width: itemSize, height: itemHeight)
layout.minimumInteritemSpacing = 3
layout.minimumLineSpacing = 3
soundboard.collectionViewLayout = layout
navigationItem.rightBarButtonItem = editButtonItem
if(traitCollection.forceTouchCapability == .available){
registerForPreviewing(with: self as UIViewControllerPreviewingDelegate, sourceView: collectionView)
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
loadData()
}
func loadData() {
let soundRequest:NSFetchRequest<Soundboard> = Soundboard.fetchRequest()
do {
soundBoardData = try managedObjectContext.fetch(soundRequest)
self.soundboard.reloadData()
} catch {
print("Error")
}
}
// MARK: - Collection View
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return soundBoardData.count
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> soundboardCellVC {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! soundboardCellVC
let soundItem = soundBoardData[indexPath.row]
if let getCellPhoto = soundItem.photo as Data? {
cell.title.text = "";
let cellPhoto = UIImage(data: getCellPhoto)
let cellPhotoFrame = UIImageView(image:cellPhoto)
cellPhotoFrame.frame = CGRect(x: 0, y: 0, width: cell.frame.width, height: cell.frame.height)
cellPhotoFrame.contentMode = UIView.ContentMode.scaleAspectFill
cell.backgroundView = UIView()
cell.backgroundView!.addSubview(cellPhotoFrame)
}
else
{
cell.title.text = soundItem.title;
cell.backgroundColor = UIColor(red: CGFloat(soundItem.colorRed), green: CGFloat(soundItem.colorGreen), blue: CGFloat(soundItem.colorBlue), alpha: 1)
let fontAwesomeIcon = FAType(rawValue: Helper.FANames.index(of: soundItem.icon!)!)
cell.icon.setFAIconWithName(icon: fontAwesomeIcon!, textColor: UIColor.white)
}
cell.layer.cornerRadius = 10
cell.layer.masksToBounds = true
cell.delegate = self as SoundboardCellDelegate
return cell
}
A good practice when working with cells is to clear any properties that you don't want to persist when the cell gets reused.
https://developer.apple.com/documentation/uikit/uicollectionreusableview/1620141-prepareforreuse
For example in your cells class you can do:
override func prepareForReuse() {
super.prepareForReuse()
self.title.text = nil
}
This keeps your cellForItem method cleaner, and you know for sure that the above actions will have been carried out before you reuse it.
Cells are re-used, which means that you are adding new image views to cells that have existing image views.
You should create a single UIImageView that is in your soundboardCellVC class and simply put the image into that, or set the image to nil as required.
A couple of other style points:
By convention, classes should start with an uppercase letter and it is a cell subclass, not a View Controller subclass, so it should be something like SoundboardCell not soundboardCellVC
Finally, your cellForItemAt signature should match the protocol declaration and be declared as returning a UICollectionViewCell, not your specific cell subclass:
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {

Complex Image based Button Layout

I'm fairly new to programming in general, I'm trying to figure out how to create numerous buttons in a complex layout. My layout is basically a transparent PNG of a body cut into about 24 sections and I want each segment of the body to be a separate button.
I've tried a few layouts in the view controller. Setting up a ton of buttons overlaying an image (couldn't keep the layout straight when launching in the simulator) and I've tried giving the buttons images, I've tried large image sized buttons but I could only use the top most button.
Is there any way to do this, or is it going to need code to be doable?
UICollectionViewController or UICollectionView is a better choice for you. You can set the UICollectionViews background image and regard the cells as your buttons.
Here is a quick example.
import UIKit
class Collection: UICollectionViewController {
private let cellId = "cell"
override init(collectionViewLayout layout: UICollectionViewLayout) {
if let l = layout as? UICollectionViewFlowLayout{
l.minimumInteritemSpacing = 0
l.sectionInset = UIEdgeInsetsZero
l.scrollDirection = .Vertical
l.footerReferenceSize = CGSize.zero
l.headerReferenceSize = CGSize.zero
let screenWidth = UIScreen.mainScreen().bounds.width
let itemWidth = CGFloat(Int(screenWidth/4))
l.itemSize = CGSize(width: itemWidth, height: itemWidth)
}
super.init(collectionViewLayout: layout)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.whiteColor()
collectionView?.registerClass(UICollectionViewCell.self, forCellWithReuseIdentifier: cellId)
}
}
extension Collection{
override func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 4
}
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 4
}
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(cellId, forIndexPath: indexPath)
cell.backgroundColor = UIColor.redColor()
return cell
}
}
If you want to add touch action to a cell.
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
}