I have 2 swift files the one is my HomeViewController and the second is my EventCollectionViewCell. In the second one I have an IBOutlet = informationView :UIView and I want to access this from the HomeViewController.
Is there any way ?
Thank you in advance,
KS
Well I suppose that in HomeViewController you have a UICollectionView and that acts as a datasource for it, then in your collectionView dataSource you can make:
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cellIdentifier", forIndexPath: indexPath) as! EventCollectionViewCell
//Here you can access the IBOulets define in your cell
cell.informationView.backgroundColor = UIColor.greenColor()
return cell
}
Edit:
Problem: When you tap on a cell you want to show an Overlay inside the cell.
Solution:
You need you data model to maintain the state, if it's active or not (the informationView), suppose that ItemCell is my model (in your case Event can be):
class ItemCell{
var active:Bool = false
}
In the collectionView:cellForRowAtIndexPath:, you're going to check the current status of your model, and base on that, show or hide that overlay:
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cellIdentifier", forIndexPath: indexPath) as! EventCollectionViewCell
let event = self.data[indexPath.row]
//Here you can access the IBOulets define in your cell
cell.informationView.hidden = !event.active
return cell
}
Then as a final step, every time you select a cell (func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) method), you're going to update the status of your model:
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath){
let event = self.data[indexPath.row]
event.active = !event.active
collectionView.reloadData()
}
Sample Project:
https://github.com/Abreu0101/SampleTap
Follow what Jose has answered and if you are creating the cell in the interface builder. Set the class of the cell in Identity Inspector to EventCollectionViewCell and set the cell identifier to "cellIdentifier" that Jose has specified.
Declare a property for that cell and access IbOutlets through that object.
#property (nonatomic, strong) MSSearchHeaderView *searchHeaderView;
if (self.searchHeaderView.searchTextField.text.length <= 0) {
self.searchHeaderView.clearButton.hidden = YES;
}else{
self.searchHeaderView.clearButton.hidden = NO;
}
Related
I have a UICollectionViewController, which scrolls vertically(like a tableview). I created a custom UICollectionViewCell. Inside of a custom cell, there are checkmarks. I need some kind of event to click when the user clicks on a checkmark.
What I tried, was overriding:
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("clicked")
}
but that only executes when the use clicks on a cell. But, asI stated above, the cell contains checkmarks...I need to find out when each individual check mark is clicked.
Is this possible?
You can add an IBAction to the UICollectionViewCell class and handle the tap from that IBAction, if you need it to change something on the Parent view controller you have a couple of options, you can use a delegate or pass the controller to the cell.
On the parent view controller :
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath) as! CustomCollectionViewCell
cell.controller = self
return cell
}
On the UICollectionViewCell class:
var controller: ParentViewController?
#IBAction func checkmarkPressed(sender: UIButton) {
print("checkmarkPressed")
controller.someFunction()
}
I have a UICollectionView with flow layout, about 140 cells each with a simple UITextView. When a cell is recycled, I pop the textView onto a cache and reuse it later on a new cell. All works well until I reach the bottom and scroll back up. At that point I can see that the CollectionView vends cell number 85, but then before cell 85 is displayed it recycles it again for cell 87 so I now lose the content of the cell I had just prepared.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "FormCell", for: indexPath) as! FormCollectionViewCell
let textView = Cache.vendTextView()
textView.text = "\(indexPath.row)"
cell.addSubview(textView)
cell.textView = textView
return cell
}
And on the UIcollectionViewCelC
override func prepareForReuse() {
super.prepareForRuse()
self.textView.removeFromSuperView()
Cache.returnView(self.textView)
}
I would have thought that after cellForItemAtIndexPath() was called, it would then be removed from the reusable pool of cells but it seems it is immediately being recycled again for a neighbouring cell. maybe a bug or I am possibly misunderstanding the normal behaviour of UICollectionView?
As I understand it, what you're trying to do is just keep track of cell content - save it when cell disappears and restore it when it comes back again. What you're doing can't work well for couple of reasons:
vendTextView and returnView don't take indexPath as parameter - your cache is storing something and fetching something, but you have no way of knowing you're storing/fetching it for a correct cell
There's no point in caching the whole text view - why not just cache the text?
Try something like that:
Have your FormCollectionViewCell just have the text view as subview, and modify your code like so:
class YourViewController : UIViewController, UICollectionViewDataSource, UICollectionViewDelegate
{
var texts = [IndexPath : String]()
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "FormCell", for: indexPath)
if let formCell = cell as? FormCollectionViewCell {
cell.textView.text = texts[indexPath]
return cell
}
}
func collectionView(_ collectionView: UICollectionView,
didEndDisplaying cell: UICollectionViewCell,
forItemAt indexPath: IndexPath)
{
if let formCell = cell as? FormCollectionViewCell {
{
texts[indexPath] = formCell.textView.text
}
}
}
I am creating a project where I have a lot of options for user to select among different plans.
So I created a UITableViewCell, put UICollectionView inside with one UICollectionViewCell as one plan.
Now user can select only one plan out of all.
Let's say the user selects the second plan, I need to update the price on a button which is in UITableViewCell.
I know we can reload tableCell and update. But is there any other way to do except reloading UITableViewCell
Let me know the best way to do that except reloading whole tableviewcell
In didselect method of Uicollectionview, get the tableviewcell of price displaying and update the button.
CollectionView didSelectItem method
{
NSIndexPath *indexpathoftableviewcell = [NSIndexPath indexpathForRow:1 inSection:0];
UITableViewCell *priceCell = [tableView cellForRowAtIndexPath: indexpathoftableviewcell];
UIButton *priceBtn = (UIButton*)[priceCell viewWithTag:tagOfBtn];
[priceBtn setTitle:#"43432" forControlState:UIControlStateNormal];
}
Assuming your UITableViewCell loads the UICollectionView inside it, so the obvious choice for UICollectionView's DataSource and Delegate should be your UICollectionViewCell.
So in your TableView Cell you can simply access the label that you need to update with price as shown below
extension MyTableViewCell : UICollectionViewDataSource {
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 100
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let someCell : MyCollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "your_identifier", for: indexPath) as! MyCollectionViewCell
return someCell
}
}
extension MyTableViewCell : UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath) as! MyCollectionViewCell
self.priceLabel.text = "\(cell.value)"
}
}
here I am assuming that your CollectionViewCell is a custom cell and has a property named value where it store the price of plan
Accommodate the Plans (UICollectionView) & Price (UILabel or UIButton) in the UITableViewCell rather than making it as separate cells
Here is the reason behind it.
Both Price and Plans are tightly coupled. It means, When you change plan the respective price should come and this requirement may grow in future like adding some more views. If you accommodate the entire part in one UITableViewCell, it will become very easy. This way you can manage your design future proof.
regarding the question, the price is just like one more IBOutlet in the CustomTableViewCell. Just update the price in
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {}
I'm trying to implement a custom cell with support for user tapping. Previously the functions related are:
func collectionView(collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueResuableCell(withReuseIdentifier: "CustomCell", for: indexPath) as! CustomCell
cell.setEmpty() // to init the cell
return cell
}
func collectionView(collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath) as ! CustomCell
//implementations
self.collectionView.reloadItem(at: [indexPath])
}
Then I noticed that after the tapping, the second function gets called first, but the first one also gets called afterwards, which means after tapping my cell will still be set to empty, so I changed the first function to this:
let cell = collectionView.dequeueResuableCell(withReuseIdentifier: "CustomCell", for: indexPath) as! CustomCell
if cell.valueInside == nil {
cell.setEmpty() // this will set valueInside to be a non-nil value
}
return cell
But it's still not working properly. I tracked the process: when loading the UI for the first time, init cell first (with the setEmpty() method); then after the tapping, cell is updated, and then the first function is called, but the cell obtained by this
collectionView.dequeueResuableCell(withReuseIdentifier: "CustomCell", for: indexPath) as! CustomCell
shows that the value inside is still nil, so the cell is not really up-to-date. How should I fix this? Or is my implementation logical (should I init the cell somewhere else instead of using this
check if it's nil -> then init
logic)?
func collectionView(collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
is called every time your inquire for a cell like
collectionView.cellForItem(at: indexPath)
or
self.collectionView.reloadItem(at: [indexPath])
The best way to use is to declare a class level variable like an array to hold the backed data, then for cell creating get data from that array and in your didSelectItemAt update that array and then just force the collection view to update that cell only.
// In your class scope
private var arData: [String] = ["One", "Two", "Three"] // This could be populated in viewDidLoad as well
// .... rest of your code
func collectionView(collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueResuableCell(withReuseIdentifier: "CustomCell", for: indexPath) as! CustomCell
//...
if indexPath.row < arData.count {
cell.valueInside = arData[indexPath.row]
} else {
cell.valueInside = "Default Value"
}
return cell
}
// ....
func collectionView(collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
//implementations
if indexPath.row < arData.count {
arData[indexPath.row] = "newValue"
self.collectionView.reloadItem(at: [indexPath])
}
}
This is the logic on how to correctly change a cell view content now if you want to change it's appearance again you should set the flag or whatever status distinguishing mechanism you have in the didSelectItemAt and just reload the cell considering cellForItemAt will refer to that status and apply's that appearance change.
Right now I have a list of scrolling usernames using a Collection View of buttons. But I’d like to add overlapping delete buttons to each row. They'd need to be attached to the name buttons and scroll with them.
How can I add these buttons to my CollectionView?
(Also I'd like to skip the delete button on the first row for obvious reasons)
Current Code:
//Add the cells to collection
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell: UsernameCollectionViewCell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath) as! UsernameCollectionViewCell
cell.usernameLabel.text = userNames [indexPath.row]
return cell
}
//Upon Selecting an item
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
if (indexPath.row == 0){
self.performSegueWithIdentifier("newUserSegue", sender: self)
}
else {
sendData(userNames[indexPath.row])
self.dismissViewControllerAnimated(true, completion: nil)
}
}
Got it working! Here's how:
I added a button to the cell in the Storyboard.
Connected an outlet to the UICollectionViewCell class.
Edited view controller code to:
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell: UsernameCollectionViewCell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath) as! UsernameCollectionViewCell
cell.usernameLabel.text = userNames [indexPath.row]
cell.deleteButton?.layer.setValue(indexPath.row, forKey: "index")
cell.deleteButton?.addTarget(self, action: "deleteUser:", forControlEvents: UIControlEvents.TouchUpInside)
// Remove the button from the first cell
if (indexPath.row == 0){
var close : UIButton = cell.viewWithTag(11) as! UIButton
close.hidden = true
}
return cell
}
func deleteUser(sender:UIButton) {
let i : Int = (sender.layer.valueForKey("index")) as! Int
userNames.removeAtIndex(i)
UserSelectCollection.reloadData()
}
Many thanks to JigarM for his examples on GitHub:
https://github.com/JigarM/UICollectionView-Swift
Why not create custom UICollectionViewCell in IB and just add button to it ?
Register it to your collectionView with :
- registerNib:forCellReuseIdentifier:
You can use delegate or notification to process button tap.