is it possible to handle UICollectionViewCell if i click UIButton outside UICollectionView - ios

First, I'm sorry but i'm not good at English.
Anyway, i want to select all cells in UICollectionViewCell when i click UIButton outside UICollectionView. But i don't how can i do.
I think it will be possible using collections of UIButton like 5 stars. i just try it.
well.. click button below. The cells in blue is false.
Main view
if you click 'Everyday' Button, All cells will be red. The cells in red means it is chosen.
Selected Everyday
if you click 'Weekend' Button, the two rightmost cells will be red. now you know this UICollectionView means 'a week'. so, when 'Weekend' is selected, other 5 days will be blue.
Selected Weekend
i made this with UICollectionView. but i don't know how i control with button outside. if you need more information and code, or you don't understand i'll edit it or leave a comment. please tell me how.
ViewController code about UIButton and UICollectionView is here. and there is a imageview in CollectionViewCell... that's all...
import UIKit
class CreateViewController: UIViewController {
#IBOutlet weak var titleTextField: UITextField!
#IBOutlet weak var MotivTextField: UITextField!
#IBOutlet weak var repeatCollectionView: UICollectionView!
#IBAction func everydayBtn(_ sender: Any) {
// empty
}
#IBAction func weekdayBtn(_ sender: Any) {
// empty
}
#IBAction func weekendBtn(_ sender: Any) {
// empty
}
var selectedDay = [Bool]()
override func viewDidLoad() {
super.viewDidLoad()
for _ in 0...7 {
self.selectedDay.append(false)
}
repeatCollectionView.delegate = self
repeatCollectionView.dataSource = self
repeatCollectionView.isScrollEnabled = false
}
}
extension CreateViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if let cell = collectionView.cellForItem(at: indexPath) as? RepeatDayCVCell {
cell.dayBtn.backgroundColor = UIColor.red
}
}
}
extension CreateViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 7
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "RepeatDayCVCell", for: indexPath) as! RepeatDayCVCell
if self.selectedDay[indexPath.row] == true {
cell.dayBtn.backgroundColor = UIColor.red
}
else {
cell.dayBtn.backgroundColor = UIColor.blue
}
return cell
}
}

You will have to update your data model and reload the collection view on each button action.
#IBAction func weekendBtn(_ sender: Any) {
for index in 0..<selectedDay.count {
if index < 5 {
selectedDay[index] = false
} else {
selectedDay[index] = true
}
}
repeatCollectionView.reloadData()
}
#IBAction func weekdayBtn(_ sender: Any) {
for index in 0..<selectedDay.count {
if index < 5 {
selectedDay[index] = true
} else {
selectedDay[index] = false
}
}
repeatCollectionView.reloadData()
}
#IBAction func everydayBtn(_ sender: Any) {
for index in 0..<selectedDay.count {
selectedDay[index] = true
}
repeatCollectionView.reloadData()
}
If you want to update the collection view when tapping on cell, update the data in selectedDay at that index and reload that row.
Pls excuse the formatting.

You can manipulate your model, I mean about array selectedDay. It depends on the case - you can set true/false at specific indexes. After an update the array, you have to reload data using reloadData() method to see the effect at UI layer.
#IBAction func everydayBtn(_ sender: Any) {
for i in 0...7 {
self.selectedDay[i] = true
}
repeatCollectionView.reloadData()
}
#IBAction func weekdayBtn(_ sender: Any) {
for i in 0...7 {
self.selectedDay[i] = i < 5 ? true : false
}
repeatCollectionView.reloadData()
}
#IBAction func weekendBtn(_ sender: Any) {
for i in 0...7 {
self.selectedDay[i] = i > 5 ? true : false
}
repeatCollectionView.reloadData()
}
Method collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) is unneeded now. If you tap on any cell, it changes the colour to red.

Related

Swift - Issue accessing data from UICollectionView embedded in UITableView

I have a tableView, with a prototype cell containing a UICollectionView. I’ve setup the tableView according to this tutorial (https://medium.com/#stasost/ios-how-to-build-a-table-view-with-multiple-cell-types-2df91a206429), and the UI is working. I can pass data through my tableView and into the collectionView.
View Layout
When a collectionViewCell is selected it segues to another view.
I haven’t figured out how to access the data from the collectionViewCell and pass it to the new view.
The collectionView is initialized within the tableView prototype cell. I've tried didSelectRow -> prepareForSegue (code below), but the commands do not autocomplete, and are not working.
Here's the code for the tableViewCell, where the collectionView is setup.
EDIT: Removed commented code for clarity
import UIKit
class homeFeedTableViewCell: UITableViewCell, UICollectionViewDelegate, UICollectionViewDataSource {
#IBOutlet weak var feedCollectionView: UICollectionView!
var selectedEvent : Event?
var collectionItems = [CollectionViewModelItem]()
var collectionItem : CollectionViewModelItem?
#IBOutlet weak var sectionHeadingLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
feedCollectionView.delegate = self
feedCollectionView.dataSource = self
print("collection items \(collectionItems.count)")
for item in collectionItems{print("type: \(item.type), title: \(item.eventTitle)")}
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
// setup view model
var item: TableViewModelItem? {
didSet {
// if not right class, skip
guard let item = item as? TableViewModelFeed else {
return
}
sectionHeadingLabel.text = item.sectionTitle
}
}
// create reuse identifier property
static var identifier: String {
return String(describing: self)
}
}
import Foundation
import UIKit
extension homeFeedTableViewCell {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
// print("dataCount3: \(collectionItems.count) \(collectionItems[collectionItems.count-1].type)")
return collectionItems.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
// let cell = UICollectionViewCell()
// return cell
self.collectionItem = collectionItems[indexPath.row]
switch collectionItem!.type {
case .yourEvents:
if let cell = collectionView.dequeueReusableCell(withReuseIdentifier:YourEventsCollectionViewCell.identifier, for: indexPath) as? YourEventsCollectionViewCell{
cell.item = collectionItem
print(cell.item?.type)
print(".yourEvents")
return cell
}
case .feed:
if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: mainFeedCollectionViewCell.identifier, for: indexPath) as? mainFeedCollectionViewCell{
cell.item = collectionItem
print(".feed")
return cell
}
}
return UICollectionViewCell()
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("\(collectionItems[indexPath.row].eventTitle) tapped")
func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "yourEventsToEventViewController" || segue.identifier == "feedToEventViewController"{
print("prepare for segue1")
let destinationVC = segue.destination as! EventViewController
if collectionItem != nil{
print("prepare for segue2")
destinationVC.backgroundImageUrl = collectionItem!.backgroundImageUrl
}
}
}
}
}
A UICollectionView keeps track of its selected indexPaths with the property indexPathsForSelectedItems. Since you trigger your segue in collectionView(didSelectItem: atIndexPath:), your selected indexPath is available during prepare(forSegue:). You could try the following:
class MyViewController: UIViewController, UICollectionViewDelegate {
...
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
performSegue(withIdentifier: "mySegue", sender: self)
}
...
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: sender)
guard let destinationVC = segue.destination as! EventViewController,
segue.identifier == "mySegue" else { return }
// In this context, your selected cell is the one who fired the segue
if let selectedIndexPaths = collectionView.indexPathsForSelectedItems,
let firstSelectedIndexPath = selectedIndexPaths.first {
let selectedObject = collectionItems[firstSelectedIndexPath.row]
destinationVC?.backgroundUrl = selectedObject.backgroundUrl
}
}
}
The sequence is:
You select a cell (through user interaction, ie tapping).
didSelect performs a segue named "mySegue" (in this example).
In prepareForSegue, you look for your selected index paths. Assuming you aren't using multi-selection, you want your first and only indexPath. Using that index path, you can retrieve your data in your collectionItems array.

unwind from tap in cell collectionView

I've been trying for hours but I do not understand how I hope someone can help me!
In practice I have an initial VC I click on an image that takes me to a collectionView through a follow and the 2 elements are also joined by a NC.
In the CollectionView are inserted images, which are contained in an array, I would like to touch on an image to return to the initial VC and that the image displayed is the one selected in the CollectionView.
I tried with UnWind but I can not carry the information of the image index that I try to recover in the didselct.
Viewcontroller
class ViewController: UIViewController {
#IBOutlet weak var immagine: UIImageView!
#IBAction func actionTap(_ sender: UITapGestureRecognizer) {
print("tap")
performSegue(withIdentifier: "selezione", sender: nil)
}
#IBAction func indietro(segue: UIStoryboardSegue){
let recupero = segue.source as! CollectionViewController
print(recupero.indice)
immagine.image = UIImage(named: recupero.arrayImmagini[recupero.indice])
}
private let reuseIdentifier = "Cell"
...
}
CollectionViewController
class CollectionViewController: UICollectionViewController {
var arrayImmagini = ["globe","bed","home","toolbox"]
var indice = 0
override func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return arrayImmagini.count
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! CollectionViewCell
cell.imgCell.image = UIImage(named: arrayImmagini[indexPath.row])
return cell
}
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
// print(arrayImmagini[indexPath.row])
let indice = indexPath.row
// idImg = indexPath.row
}
...
}
even if set index = indexPath.row it is never recovered in the unwind
You have wired the unwind segue to your collection view cell. The unwind happens before didSelectItemAt runs.
You can fix this in one of two ways:
Remove the segue from the cell, and wire it from the viewController icon (top left icon) to the exit icon (top right icon). Find this exit segue in the Document Outline and give it an identifier such as "returnToMain". In didSelectItemAt, call self.performSegue(withIdentifier: "returnToMain", sender: self) after setting indice.
OR
Don't use didSelectItemAt at all. Instead, implement prepare(for:sender:):
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "returnToMain" {
if let indexPath = self.collectionView.indexPathsForSelectedItems?.first {
indice = indexPath.row
}
}
}
You should implement some handler for this purpose or delegate method. For example:
class ViewController: UIViewController {
...
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "yourCollectionViewController" {
if let vc = segue.destination as? CollectionViewController {
vc.imageHandler = { imageIndex in
...
}
}
}
}
}
class CollectionViewController: UICollectionViewController {
var imageHandler : ((_ imageId: Int) -> Void)?
...
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
// print(arrayImmagini[indexPath.row])
let indice = indexPath.row
// idImg = indexPath.row
imageHandler?(indice)
dismiss(animated: true, completion: nil)
}
}

Passing data from a child VC back to a Collection View Cell

I have two ViewController: MainVC, and ChildVC. The MainVC includes a CollectionView with 5 cell. Each of these cell segues to the ChildVC. On this ChildVC, you can select different items which increases (or decreases) a counter on the ChildVC (the counter just reads "## selected".)
Basically, I just want this counter data on the ChildVC to be passed back onto a label of the respective MainVC cell that was tapped. For example: If user taps the second cell on the MainVC, selects 13 items on the ChildVC, then returns back to the MainVC, there will be a "13" in a label on the second cell. Then if the user taps the first cell, selects 5 items on the ChildVC, then returns back to the MainVC, there will be a "5" in a label on the first cell along with the "13" on second cell.
My progress:
I have decided that delegation is an appropriate solution for my requirements, as delegation makes it easy to pass data to/from VC's. I need assistance in passing data BACK from a ChildVC TO a CollectionView Cell.
My questions:
Along with the selected counter count (Int), what other information should be passed to and from within the protocol? (I wasn't sure if the indexPath should be passed, so that the data displays on the correct cell on the MainVC?)
On the MainVC, should the data received from the protocol ChildVC be sent to the CollectionViewCell? or the MainVC cellForItemAt method?
Update:
I have some progress below. But it's not working as intended.
In the below code, I have created both the ViewController (MainVC) and ChildVC. In the Child VC, there is a UISlider to emulate the selected counter. I would like this counter data passed back to the respective MainVC CollectionView Cells. What's happening now is the MainVC CollectionView gets a new cell added once I change the value of the slider! The 'Clear All Animals' btn needs to "zero out" the slider data for all the cells, but I haven't gotten that far yet..
View Controller (MainVC in my question above)
class ViewController: UIViewController {
var allAnimals = AnimalData.getAllAnimals()
#IBOutlet weak var mainCV: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
mainCV.dataSource = self
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "AnimalSegue" {
let childVC = segue.destination as! ChildVC
childVC.delegate = self
if let indexPath = self.mainCV.indexPath(for: sender as! AnimalCollectionViewCell) {
let animalData = self.allAnimals[indexPath.item]
childVC.animal = animalData
childVC.indexPath = indexPath
}
childVC.allIndexPaths = getAllIndexPaths()
}
}
func getAllIndexPaths() -> [IndexPath] {
var indexPaths: [IndexPath] = []
for i in 0..<mainCV.numberOfSections {
for j in 0..<mainCV.numberOfItems(inSection: i) {
indexPaths.append(IndexPath(item: j, section: i))
}
}
return indexPaths
}
}
extension ViewController: DataDelegate {
func zeroOut(for animalObject: AnimalModel, at indexPath: [IndexPath]) {
print("ZERO OUT")
self.mainCV.reloadData()
}
func updatedData(for animalObject: AnimalModel, at indexPath: IndexPath ) {
self.allAnimals[indexPath.item] = animalObject
self.mainCV.reloadItems(at: [indexPath])
}
}
extension ViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return allAnimals.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "AnimalCell", for: indexPath as IndexPath) as! AnimalCollectionViewCell
let animal = allAnimals[indexPath.item]
cell.animal = animal
return cell
}
}
ChildVC
class ChildVC: UIViewController {
#IBOutlet weak var animalTitleLabel: UILabel!
#IBOutlet weak var labelCounter: UILabel!
#IBOutlet weak var sliderLabel: UISlider!
var delegate: DataDelegate?
var animal: AnimalModel?
var indexPath: IndexPath?
var allIndexPaths: [IndexPath]?
override func viewDidLoad() {
super.viewDidLoad()
animalTitleLabel.text = animal?.name
animalTitleLabel.textColor = animal?.color ?? .white
sliderLabel.value = Float(animal?.amountCounter ?? 0)
self.labelCounter.text = "\(Int(sliderLabel.value))"
}
#IBAction func closeButtonPressed(_ sender: UIButton) {
if let delegate = self.delegate,
let indexPath = self.indexPath,
let animal = self.animal {
delegate.updatedData(for: animal, at: indexPath)
}
self.dismiss(animated: true, completion: nil)
}
#IBAction func sliderChanged(_ sender: UISlider) {
let newValue = Int(sender.value)
labelCounter.text = "\(newValue)"
self.animal?.amountCounter = newValue
}
#IBAction func clearAllBtnPressed(_ sender: UIButton) {
if let delegate = self.delegate,
let all = self.allIndexPaths,
var animal = self.animal {
animal.amountCounter = 0
delegate.zeroOut(for: animal, at: all)
}
self.dismiss(animated: true, completion: nil)
}
}
Animal Collection View Cell
class AnimalCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var animalLabel: UILabel!
#IBOutlet weak var counterLabel: UILabel!
var animal: AnimalModel! {
didSet {
self.updateUI()
}
}
func updateUI() {
animalLabel.text = animal.name
counterLabel.text = "\(animal.amountCounter)"
self.backgroundColor = animal.color
}
}
Data
struct AnimalData {
static func getAllAnimals() -> [AnimalModel] {
return [
AnimalModel(name: "Cats", amountCounter: 0, color: UIColor.red),
AnimalModel(name: "Dogs", amountCounter: 0, color: UIColor.blue),
AnimalModel(name: "Fish", amountCounter: 0, color: UIColor.green),
AnimalModel(name: "Goats", amountCounter: 0, color: UIColor.yellow),
AnimalModel(name: "Lizards", amountCounter: 0, color: UIColor.cyan),
AnimalModel(name: "Birds", amountCounter: 0, color: UIColor.purple)
]
}
}
Delegate
protocol DataDelegate {
func updatedData(for animalObject: AnimalModel, at: IndexPath)
func zeroOut(for animalObject: AnimalModel, at: [IndexPath])
}
Screenshots below of what is happening. See how Dogs is being added as another cell with the value of 23? What should happen is the 0 should change to a 23 on the second blue Dogs cell. I don't understand updating the data source and reloading the correct cells??
How do i simply pass back the slider data into the cell that was originally tapped?
Any help is appreciated
You have the right idea with your delegation, but you need to be able to provide context back to your delegate; ie. what animal was being updated? To do this, either MainVC needs to keep a property of the item that is being updated, or this information needs to be provided to the ChildVC so that it can provide the information back to the MainVC. I will use the latter approach.
Protocol
protocol DataDelegate {
func updatedData(for animalObject: AnimalModel, at: IndexPath)
func clearAll()
}
MainVC
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "AnimalSegue" {
let childVC = segue.destination as! ChildVC
childVC.delegate = self
if let indexPath = self.mainCV.indexPath(for: sender as! AnimalCollectionViewCell) {
let animalData = self.allAnimals[indexPath.item]
childVC.animal = animalData
childVC.indexPath = indexPath
}
}
}
extension ViewController: DataDelegate {
func updatedData(for animalObject: AnimalModel, at indexPath: IndexPath ) {
self.allAnimals[indexPath.item] = animalObject
self.mainCV.reloadItems(at: [indexPath])
}
func clearAll() {
for index in 0..<self.allAnimals.count {
self.allAnimals[index].count =0
}
self.mainCV.reloadData()
}
ChildVC
class ChildVC: UIViewController {
#IBOutlet weak var animalTitleLabel: UILabel!
#IBOutlet weak var labelCounter: UILabel!
#IBOutlet weak var sliderLabel: UISlider!
var delegate: DataDelegate?
var animal: AnimalModel?
var indexPath: IndexPath?
override func viewDidLoad() {
super.viewDidLoad()
animalTitleLabel.text = animal?.name
animalTitleLabel.textColor = animal?.color ?? .white
sliderLabel.value = animal?.count ?? 0
self.labelCounter.text = "\(Int(sliderLabel.value))"
}
#IBAction func closeButtonPressed(_ sender: UIButton) {
if let delegate = self.delegate,
let indexPath = self.indexPath,
let animal = self.animal {
delegate.updatedData(for: animal, at: indexPath)
}
self.dismiss(animated: true, completion: nil)
}
#IBAction func sliderChanged(_ sender: UISlider) {
let newValue = Int(sender.value)
labelCounter.text = "\(newValue)"
self.animal.count = newValue
}
#IBAction func clearAllBtnPressed(_ sender: UIButton) {
delegate.clearAll()
}
}
Updated
I have updated my answer to show how you could implement the clear all. In this case there is no reason to have the ChildVC update the data model; it simply needs to invoke a delegate method to let the MainVC know that it should update the model and refresh the collection view.
I think that this gives a hint as to why the ChildVC is the wrong place for the "clear all" button; if the code feels a bit clunky then the user experience may be a bit clunky too. The clear all button should just be on your MainVC - it doesn't make sense for a button on a animal-specific view to be affecting other animals. It also isn't "discoverable"; I don't find out about the clear all until I select an animal. I realise that this is just a "learning app" but user experience is an important part of iOS app development and so it is never too early to consider it; it can also impact the way you design your code as you can see here.
So this is a very incomplete display, but i believe this solution is something that you are lookin for
class mainVC {
var counters: [Int] = [0,0,0,0,0]
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(forIndexPath: indexPath) as CustomCell
cell.counterLabel = counters[indexPath.item]
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let childVC = ChildVC()
childVC.finishedSelecting = { counter in
self.counters.insert(counter, at: indexPath.item)
childVC.dismiss()
self.theCollectionView.reloadItems(at: [indexPath])
//Or
self.theCollectionView.reloadData()
}
present(childVC, animated: true)
}
}
class childVC {
var finishedSelecting: ((Int) -> ())?
var counter = 5
#objc func finishedButtonPressed() {
finishedSelecting?(counter)
}
func count() {
counter+=1
}
}

TableView Only Updates after Screen Rotates

Image of the tableview
I have a tableview with a collection view in each cell, all linked to an array. Each collection view has tags, so when I have stuff in the array from the beginning, all tableview cells and collection view cells appear properly in the app. But when I add an element to the array in the app itself (I have a second view controller with the stuff to do that), it works but the new table view cell only appears after the screen rotates (really odd). I have tried adding an object of the view controller with the table view in the second view controller where I add an element to the array. Then in the second view controller in ViewWillDisappear, I reloadData() through that object like this:
var vc : ViewController? = ViewController()
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
vc?.listOfActs.reloadData()
}
But this results in an EXC_BAD_INSTRUCTION
Then I tried adding self.listOfActs.reloadData() in the prepareForSegue in the view controller with the table view just so that I could see that it at least refreshes the data at some point in time but even that doesn't work when I click on add scene a second time.
UPDATE: New MainViewController
This is the new first view controller with the table view. I renamed it and have implemented the method for adding to array and reloading. It kind of works if I use an if let on the reloadData but then I'm back to square one where it only updates when I rotate the screen. When I get rid of the if let so it can actually try to update the table view, it gives me a Fata error: unexpectedly found a nil while unwrapping.
class MainViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
//The Table View
#IBOutlet var AddActButton: UIButton!
#IBOutlet weak var listOfActs: UITableView!
var sectionTapped : Int?
var indexitemTapped : Int?
override func viewDidLoad() {
super.viewDidLoad()
listOfActs.delegate = self
listOfActs.dataSource = self
}
//Table View Functions
func numberOfSections(in tableView: UITableView) -> Int {
return actsCollection.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "actCell", for: indexPath) as! ActCell
cell.setCollectionViewDataSourceDelegate(self, forSection: indexPath.section)
return cell
}
//Add To Table View
func addObjects(appendage: Act) {
actsCollection.append(appendage)
if let shit = listOfActs {
shit.reloadData()
}
}
//Header Cell
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let cellHeader = tableView.dequeueReusableCell(withIdentifier: "headerCell") as! HeaderCell
cellHeader.headerName.text = actsCollection[section].actName
return cellHeader
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 40
}
}
//Scene Collection in Act Cell
extension MainViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return actsCollection[collectionView.tag].actScenes.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "sceneCell", for: indexPath) as! SceneCell
cell.sceneTitle.text = actsCollection[collectionView.tag].actScenes[indexPath.item].sceneTitle
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
sectionTapped = collectionView.tag
indexitemTapped = indexPath.item
performSegue(withIdentifier: "showDetail", sender: self)
}
//Segue Prepare
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showDetail" {
let detailsVC = segue.destination as! SceneDetailController
detailsVC.textToAppearInSceneName = actsCollection[sectionTapped!].actScenes[indexitemTapped!].sceneTitle
}
}
}
UPDATE:New second view controller, the one that adds to the array.
class AddActController: UIViewController, UITextFieldDelegate {
#IBOutlet var sceneLiveName: UILabel!
#IBOutlet var sceneNameTextField: UITextField!
#IBOutlet var sceneDescriptionTextField: UITextField!
#IBOutlet var AddSceneButton: UIButton!
#IBOutlet var cardBounds: UIView!
var newName: String? = ""
#IBOutlet var cardShadow: UIView!
var shit = MainViewController()
override func viewDidLoad() {
super.viewDidLoad()
sceneNameTextField.delegate = self
AddSceneButton.alpha = 0.0
cardBounds.layer.cornerRadius = 20.0
cardShadow.layer.shadowRadius = 25.0
cardShadow.layer.shadowOpacity = 0.4
// Do any additional setup after loading the view.
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
UIView.animate(withDuration: 0.2){
self.AddSceneButton.alpha = 1.0
}
}
#IBAction func exitButton(_ sender: UIButton) {
dismiss(animated: true, completion: nil)
}
#IBAction func addSceneButton(_ sender: UIButton) {
if newName == "" {
sceneLiveName.text = "Enter Something"
sceneNameTextField.text = ""
}
else {
let appendAct: Act = Act(actName: newName!, actTheme: "Action", actScenes: [Scene(sceneTitle: "Add Act", sceneDescription: "")])
shit.addObjects(appendage: appendAct)
dismiss(animated: true, completion: nil)
}
}
//MARK: textField
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let text: NSString = (sceneNameTextField.text ?? "") as NSString
let resultString = text.replacingCharacters(in: range, with: string)
sceneLiveName.text = resultString
newName = String(describing: (sceneLiveName.text)!.trimmingCharacters(in: .whitespacesAndNewlines))
return true
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
sceneNameTextField.resignFirstResponder()
return true
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
Here is the class for the uitableviewcell that contains its own collection view.
class ActCell: UITableViewCell {
#IBOutlet fileprivate weak var sceneCollection: UICollectionView!
}
extension ActCell {
func setCollectionViewDataSourceDelegate<D: UICollectionViewDataSource & UICollectionViewDelegate>(_ dataSourceDelegate: D, forSection section: Int) {
sceneCollection.delegate = dataSourceDelegate
sceneCollection.dataSource = dataSourceDelegate
sceneCollection.tag = section
sceneCollection.reloadData()
}
}
And here is the model with the user's data including the acts and scenes.
struct Scene {
var sceneTitle: String
var sceneDescription: String
//var characters: [Character]
//var location: Location
}
struct Act {
var actName: String
var actTheme: String
var actScenes : [Scene] = []
}
var actsCollection : [Act] = [
Act(actName: "dfdsfdsfdsf", actTheme: "Action", actScenes: [Scene(sceneTitle: "Example Act", sceneDescription: "")])
]
Any help is greatly appreciated. Thank you to all.
So if I'm not mistaken I believe the viewDidLoad method gets call during screen rotations. So this explains why it update during so. Now to get it to update without rotating the device, I would add an observer in the notificationCenter to watch for any updates to the tableView then call a #selector to do the reloadData(). So here is an example of this. In the viewDidLoad method add
NotificationCenter.default.addObserver(self, selector: #selector(refreshTable), name: NSNotification.Name(rawValue: "load"), object: nil)
Then add the method refreshTable()
func refreshTable() {
listOfActs.reloadData()
}
This is basically how I handle keeping the tableView refreshed.
Well - viewDidLoad is loaded only for the first time controller loads his view (not sure about rotation).
If you really need - you can reload tableView in viewWillAppear - but I wouldn't do this.
Instead of
actsCollection.append(appendAct)
dismiss(animated: true, completion: nil)
create a method on the first controller like addObjectToList(appendAct) and in that method, just easily append object to your list array and reload tableView after adding.
You will be reloading tableView only when you really add something to your list and not every time controller appears, you also don't need notification observer.
EDIT - UPDATE
What is this?
if newName == "" {
sceneLiveName.text = "Enter Something"
sceneNameTextField.text = ""
}
else {
let appendAct: Act = Act(actName: newName!, actTheme: "Action", actScenes: [Scene(sceneTitle: "Add Act", sceneDescription: "")])
shit.addObjects(appendage: appendAct)
dismiss(animated: true, completion: nil)
}
I mean - what is shit.AddObjects? Shit is defined as tableView - but you have to call this method on instance of your controller.
Another thing - change your setup from sections == number of items with 1 row to be one section with number of rows == number of items. :)

How to change text of Label in UITableView based on button pressed?

I have a UITableView in ViewController which displays Default, five times on running in the simulator based on the following code.
class ViewController: UIViewController,UITableViewDataSource,UITableViewDelegate {
#IBOutlet weak var myTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
}
func tableView(_ tableView:UITableView, numberOfRowsInSection section:Int) -> Int
{
return 5
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let myCell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! OutsideCell
myCell.titleLabel.text = "Default"
return myCell
}
#IBAction func firstButtonPressed(_ sender: Any){
}
#IBAction func secondButtonPressed(_ sender: Any) {
}
}
I also have 2 buttons at the bottom of ViewController and
I like to display text as First Button when firstButtonPressed and Second Button when secondButtonPressed instead of Default in UITableView. How is this possible?
You can loop through each cell of the table view when the button is tapped.
for cell in myTableView.visibleCells() {
cell.titleLabel?.text = "First Button"
}
You should use a generic method as eventButtonPressed and use the tag attribute to determine which button is pressed through the storyboard.
#IBAction func eventButtonPressed(_ sender: UIButton) {
if (sender.tag == 0) {
//First Button
} else if (sender.tag == 1) {
//Second Button
}
}
You can use switch operand if you have many cases.
switch sender.tag {
case 0:
print("First button pressed")
case 1:
print("Second button pressed")
default:
print("Default button pressed")
}
Hope it helps,

Resources