This question already has answers here:
Outlets cannot be connected to repeating content iOS
(9 answers)
Closed 4 years ago.
I followed the attached guide to creating static UICollectionView but now I would like to add buttons to each cell and change the text on the buttons, for example. I can not do this and get the error "UIButton is invalid. Outlets cannot be connected to repeating content." How can I fix this issue and use IBOutlets with objects in cells without leaving the ViewController?
If I need to leave ViewController please describe the process with a lot of detail as I am a beginner and am not too knowledgeable on the different view classes.
Thank you!!
Instead of the outlet between the button and the view controller, you should create a subclass of UICollectionViewCell, and add your IBOutlets on that class.
class MyCollectionViewCell: UICollectionViewCell {
#IBOutlet var myButton: UIButton!
}
Then, in Interface Builder, set this subclass to be the class of your cells (in the Identity inspector pane).
You should then be able to create the outlet connection from your button to your cell.
I hope this is clear enough. If not, please let me know!
Example code
class MyCollectionViewCell: UICollectionViewCell {
#IBOutlet var myButton: UIButton!
}
class MyViewController: UIViewController, UICollectionViewDataSource {
#IBOutlet var myCollectionView: UICollectionView!
private var isMyButtonEnabled = true
// Other view controller code
func disableMyButton() {
self.isMyButtonEnabled = false
self.myCollectionView.reloadData()
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = ... as! MyCollectionViewCell // Get cell
// Other cell setup
cell.myButton.isEnabled = self.isMyButtonEnabled
return cell
}
}
Define class like following for your collection view:
class MyCollectionCell : UICollectionViewCell {
#IBOutlet weak var likeButton: UIButton?
}
Create xib for collection cell and use above custom class for collection view.
Now in your view controller define collection view and implement following delegates UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout.
class ViewController: UIViewController, UICollectionViewDataSource,
UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
#IBOutlet var collectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
let nib = UINib(nibName: "MyCollectionViewCell", bundle: nil)
collectionView?.registerNib(nib, forCellWithReuseIdentifier: "myCell")
}
//UICollectionViewDelegateFlowLayout methods
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAtIndex section: Int) -> CGFloat
{
return 4;
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAtIndex section: Int) -> CGFloat
{
return 1;
}
//UICollectionViewDatasource methods
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int
{
return 1
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 100
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
var cell =
collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as MyCollectionCell
cell.likeButton.setTitle("myTitle", for: .normal)
cell.likeButton.tag = indexPath.row
cell.likeButton.addTarget(self, action: #selector(mainButton:), forControlEvents: .TouchUpInside)
return cell
}
#IBAction func mainButton(sender: UIButton) {
println(sender)
// use button tag to find out which button is clicked.
}
}
In above code important method is func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell where you set tag to your button and then use that tag to find out which button is pressed and use that id to find out data source or action you want to perform.
Related
I'm trying to create a simple collection view. I've got my custom cell "SectionCell" and the my custom class "Section", which contains two #IBOutlet properties: titleLabel and imageView. Both of these properties are hooked up to their respective storyboard views.
In storyboard, the collectionView scene has been linked to the MenuVC.swift file, which inherits from UICollectionView. The Cell view is linked to SectionCell. And I've set the cell's Identifier to "Section".
For debugging purposes I've set the view.backgroundColor to black and the cell's contentView background color to teal. Yet when I run the the simulator neither show. I get a white background and all that appears is the view title. Any ideas on what the fix is?
class MenuVC: UICollectionViewController {
var sections = [Section]()
override func viewDidLoad() {
super.viewDidLoad()
title = "Begin Learning"
view.backgroundColor = .black
}
// MARK:- CollectionView Methods
override func numberOfSections(in collectionView: UICollectionView) -> Int {
return 3
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Section", for: indexPath) as? SectionCell else {
fatalError("Unable to dequeue SectionCell ")
}
let section = sections[indexPath.item]
cell.titleLabel.text = section.title
cell.imageView.image = UIImage(named: section.image)
return cell
}
}
class SectionCell: UICollectionViewCell {
#IBOutlet var imageView: UIImageView!
#IBOutlet var titleLabel: UILabel!
}
class Section: NSObject {
var title: String
var image: String
init(title: String, image: String) {
self.title = title
self.image = image
}
}
Simulator
I'm also quite new to posting questions on SO. If you have any tips on how to better format questions, I'm all ears!
make outlet of your collectionview and give it to the delegate and data source...
make outlet like this in your view controller:
#IBOutlet weak var collectionView: UICollectionview!
then put this code in viewDidLoad()
override func viewDidLoad() {
super.viewDidLoad()
collectionView.delegate = self
collectionView.dataSource = self
collectionView.relaodData()
}
have you implemented the following protocol?
UICollectionViewDelegateFlowLayout
extension MenuVC: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return .init(width: 300.0, height: 300.0) // and set size for each item inside this function.
}
}
SOLVED
LOL you really do need eagle eyes as a developer. This:
override func numberOfSections(in collectionView: UICollectionView) -> Int {
return 3
}
is supposed to be:
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 3
}
And voila!
Say, I have a ViewControllerA with a tableView, and that tableView has its header view created and loaded using xib method, and that headerView contains a .horizontal scroll direction collectionView, my question is how would I update the contents of my tableView on click initiated by the collectionView inside the headerView?
similar example is in the below picture:
say, I clicked the "Research" label on the UICollectionViewCell inside the Tableview's HeaderView and the tableView reloads data of "Research", and next I clicked the "Design" label, and the tableView reloads the itself and displays data for "Design".. something like that is possible? i am stuck here.. .
link the header UICollectionView to an outlet in the storyboard, then implement all the required delegate methods.
Add this delegate method:
class ViewController: UIViewController{
var categories = [String]()
var discoverData = [discoverModel]()
var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
}
}
extension ViewController: UICollectionViewDelegate, UICollectionViewDataSource{
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
categories.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
//code to initialize cells of header collecionView
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let selectedCategory = categories[indexPath.row]
let cellToReload = discoverData.filter{return $0.serviceName == selectedCategory}[0]
guard let indexPathsOfCellToRealod = discoverData.firstIndex(of: cellToReload) else { return }
tableView.reloadRows(at: [IndexPath(index: indexPathsOfCellToRealod)], with: .automatic)
}
}
struct discoverModel: Equatable{
var image: UIImage
var serviceName: String
var name: String
var description: String
}
To make this work, just add the standard UITableView delegate methods to your ViewController
I am experimenting with UICollectionView and am only using Storyboard - no code at all. I set the number of items to 3 and gave each cell a different color and reuse identifier in the storyboard. Yet the CollectionView (pinkish background) is shown but no cells are shown. What am I missing?
Thanks in advance
-SR
Short Answer
UICollectionView doesn't support static content layouts.
Discussion
But there is a way to simulate it through code. I recommend this great article.
You don't have any code in your ViewController.swift. You can add something like this:
class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
var menuItems = ["Aap","Koe","Vis","Paard", "Leeuw", "Varken"]
#IBOutlet weak var collectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return menuItems.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
if let c = cell as? AangepasteCollectionViewCell {
c.cellLabel.text = menuItems[indexPath.row].capitalized
}
return cell
}
}
For this example you have to give the collection cell the name cell in the atribute inspector
I have made a UICollection View. In my ViewController I have the following code:
import UIKit
import AlamofireImage
class MYViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, MYItemCellDelegate {
let viewModel : MYViewModel = StandPlaceViewModel()
#IBOutlet weak var itemCollectionView: UICollectionView!
#IBOutlet weak var ItemCountLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
self.viewModel.getItems(self.viewModel.stand) { items in
self.ItemCountLabel.text = String(self.viewModel.itemCount)
self.itemCollectionView.reloadData()
}
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.viewModel.tableDict.count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
print("IN COLLECTION OF ITEMS")
let cell: MYItemCell = collectionView.dequeueReusableCellWithReuseIdentifier("itemCell", forIndexPath: indexPath) as! MYItemCell
cell.itemTitleLabel.text = self.viewModel.tableDict[indexPath.row]![0]
return cell
}
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
print("Cell \(indexPath.row) selected")
}
...
My Issue is that the UICollection never loads. IN COLLECTION OF ITEMS is never printed. I have a separate page with a collection and it all works just fine and I cant find the difference.
The ItemCountLabel.text is properly set in the viewDidLoad(), but the reloadData() never seems to call the collectionView code.
I would expect IN COLLECTION OF ITEMS to be printed twice.
Add these code into viewDidLoad
self.itemCollectionView.delegate = self;
self.itemCollectionView.dataSource = self;
I am getting this error in my code it says to "Make a symbolic breakpoint at UICollectionViewFlowLayoutBreakForInvalidSizes to catch this in the debugger." I am confused as to what it is actually asking here is the code in which I am thinking its asking it to be put in just not sure where?
import UIKit
// MARK: - CUSTOM SOCIAL CELL
class SocialCell:UICollectionViewCell {
/* Views */
#IBOutlet weak var socialIcon: UIImageView!
#IBOutlet weak var socialLabel: UILabel!
}
class SocialList: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
/* Views */
#IBOutlet weak var socialCollView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
}
// MARK: - COLLECTION VIEW DELEGATES
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return socials.count //socialNames.count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("SocialCell", forIndexPath: indexPath) as! SocialCell
cell.socialLabel.text = "\(socials[indexPath.row]["name"]!)"
cell.socialIcon.image = UIImage(named: "\(socials[indexPath.row]["name"]!)")
return cell
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
return CGSizeMake(view.frame.size.width/3.8, view.frame.size.width/3.8)
}
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
selectedSocial = "\(socials[indexPath.row]["link"]!)"
selectedName = "\(socials[indexPath.row]["name"]!)"
selectedColor = socialColors[indexPath.row]
navigationController?.popViewControllerAnimated(true)
}
}
You can systematically solve this by doing this:
Also, you should share your complete error, so that I can lead you to more specific issue. My thinking is that you have some sort of autoLayout issue in your UICollectionView
On the left of your project, click the Break Point navigator
Next click on the plus button on the bottom left and click Add Symbolic BreakPoint
Then you will be shown a popup. Add UICollectionViewFlowLayoutBreakForInvalidSizes in there like so
After this, just hit enter(on keyboard) and click anywhere
Run your code and see where the project stops
You can resolve this issue by setting 'Estimate size = Custom' of collectionView in size inspector.