After following a guide on how to add collectionViews inside a tableViewCell (here) and then I tried to see if I can switch these two cells.
However, when I try to scroll down the collectionview(horizontally) I get an error that the collectionview index goes out of bounds. I tried to see if adding a reload data for each collection view would help with that issue, but it doesn't... Code below:
class BuildTravelPackage: UIViewController, UITableViewDataSource,UITableViewDelegate, UICollectionViewDelegate {
#IBOutlet var optionsTableView: UITableView!
var items = [[PackageOption]]()
override func viewDidLoad() {
super.viewDidLoad()
let longPressRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(self.longPress(longPressGestureRecognizer:)))
self.optionsTableView.addGestureRecognizer(longPressRecognizer)
//load data into cells
}
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
cell.alpha = 0
UIView.animate(withDuration: 1.0) {
cell.alpha = 1.0
}
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 175
}
func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
return true
}
func tableView(_ tableView: UITableView, shouldIndentWhileEditingRowAt indexPath: IndexPath) -> Bool {
return false
}
func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCellEditingStyle {
return .none
}
func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
let item = items[sourceIndexPath.row]
items.remove(at: sourceIndexPath.row)
items.insert(item, at: destinationIndexPath.row)
optionsTableView.isEditing = !optionsTableView.isEditing
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = optionsTableView.dequeueReusableCell(withIdentifier: "optionCell", for: indexPath) as! PackageOptionTableViewCell
cell.collectionView.delegate = self
cell.collectionView.dataSource = self
cell.collectionView.tag = indexPath.row
cell.collectionView.reloadData()
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("Column:",indexPath.row)
print("Row:",collectionView.tag)
//highlght selected cell and load a new cell with infomation? Or load a uiview with date selection
}
func longPress(longPressGestureRecognizer: UILongPressGestureRecognizer) {
if longPressGestureRecognizer.state == UIGestureRecognizerState.began {
let touchPoint = longPressGestureRecognizer.location(in: self.view)
if let indexPath = optionsTableView.indexPathForRow(at: touchPoint) {
optionsTableView.isEditing = !optionsTableView.isEditing
}
}
}
}
extension BuildTravelPackage:UICollectionViewDataSource {
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return items[collectionView.tag].count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "singleOptionCell", for: indexPath) as! OptionCollectionViewCell
cell.image.image = items[collectionView.tag][indexPath.row].imageFile
cell.label.text = items[collectionView.tag][indexPath.row].name
return cell
}
}
I think problem is here when you are replacing objects
func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
let item = items[sourceIndexPath.row]
items.remove(at: sourceIndexPath.row)
items.insert(item, at: destinationIndexPath.row)
optionsTableView.isEditing = !optionsTableView.isEditing
}
try replacing code by
swap(& items[sourceIndexPath.row], & items[destinationIndexPath.row])
Related
I have a UITableViewCell at the very bottom of tableview, it's functionality is to add new object to the list. But I also want the user to be able to move his objects. And here comes the problem I can not solve: how can I make this UITableViewCell not movable when tableView.isEditing is true so it is always at the bottom of section, even when user tries to move it there?
Here's a simple example of preventing rows from being moved past the last row:
class ReorderViewController: UITableViewController {
var myData = ["One","Two","Three","Four","Five","Six","Seven","Eight","Nine","Don't let me move!"]
override func viewDidLoad() {
super.viewDidLoad()
let btn = UIBarButtonItem(barButtonSystemItem: .edit, target: self, action: #selector(self.startEditing(_:)))
navigationItem.rightBarButtonItem = btn
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
}
#objc func startEditing(_ sender: Any) {
isEditing = !isEditing
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return myData.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = myData[indexPath.row]
return cell
}
override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
// don't allow last row to move
return indexPath.row < (myData.count - 1)
}
override func tableView(_ tableView: UITableView, targetIndexPathForMoveFromRowAt sourceIndexPath: IndexPath, toProposedIndexPath proposedDestinationIndexPath: IndexPath) -> IndexPath {
// if user tries to drop past last row
if proposedDestinationIndexPath.row == myData.count - 1 {
// send it back to original row
return sourceIndexPath
}
return proposedDestinationIndexPath
}
override func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
let itemToMove = myData[sourceIndexPath.row]
myData.remove(at: sourceIndexPath.row)
myData.insert(itemToMove, at: destinationIndexPath.row)
}
}
I have a CollectionView that is scrolling horizontally. I have custom CollectionViewCells that have inside a TableView with three cells. It looks like this:
[![Here is a example of how it looks][1]][1]
This is the main ViewController where I have the CollectionView
extension ViewController: UICollectionViewDataSource {
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.viewModel.numberOfItems
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ItemCell.reuseIdentifier, for: indexPath) as! ItemCell
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let selectedItem = self.viewModel.items[indexPath.item]
}
}
And this is the ItemCell code that contains the tableview
class ItemCell: UICollectionViewCell, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
override func awakeFromNib() {
super.awakeFromNib()
self.tableView.delegate = self
self.tableView.dataSource = self
}
override func layoutSubviews() {
super.layoutSubviews()
self.tableView.allowsMultipleSelection = true
self.tableView.register(UINib(nibName: "DailySelectionViewCell", bundle: nil), forCellReuseIdentifier: DailySelectionViewCell.reuseIdentifier)
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func numberOfSections(in tableView: UITableView) -> Int {
return 3
}
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 20
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 60
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: DailySelectionViewCell.reuseIdentifier, for: indexPath) as! DailySelectionViewCell
switch indexPath.section {
case 0:
cell.descriptionLabel.text = "Item1"
case 1:
cell.descriptionLabel.text = "Item2"
default:
cell.descriptionLabel.text = "Item3"
}
return cell
}
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
let view = UIView(frame: CGRect.zero)
view.backgroundColor = UIColor.clear
return view
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
}
}
I want to click on the TableView cells that are inside the CollectionView cells. When I want to click on a TableViewCell, the touches are only registered on the CollectionViewCell and I can't click on any of the tableview cells inside the CollectionViewCell.
By default didSelectRowAt must work.
As an assumption DailySelectionViewCell may contains some views with user interaction, namely subclassed from UIControl: UIButton, UIDatePicker, UISegmentedControl and so on. In this case didSelectRowAt not work and this is correct behavior.
I have multiple section and each section can have multiple rows.
Code : Display as excepted.
class SampleViewController: UIViewController {
let sectionArray = ["pizza", "deep dish pizza", "calzone"]
let items = [["Margarita", "BBQ Chicken", "Peproni"], ["Margarita", "meat lovers", "veggie lovers"], ["sausage", "chicken pesto", "BBQ Chicken"]]
#IBOutlet weak var listObj: UITableView!
var selectedItems = [String]()
override func viewDidLoad() {
super.viewDidLoad()
registerCell()
// Do any additional setup after loading the view.
}
func registerCell(){
self.listObj.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
}
}
extension SampleViewController : UITableViewDelegate,UITableViewDataSource{
func numberOfSections(in tableView: UITableView) -> Int {
return sectionArray.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items[section].count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
cell.textLabel?.text = items[indexPath.section][indexPath.row]
if selectedItems.contains(items[indexPath.section][indexPath.row]) {
print("Selected Item")
cell.accessoryType = .checkmark
} else {
print("Item not selected")
cell.accessoryType = .none
}
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 44
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return sectionArray[section].uppercased()
}
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 0
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
selectedItems.append(items[indexPath.section][indexPath.row])
tableView.reloadData()
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
selectedItems.removeAll { $0 == items[indexPath.section][indexPath.row] }
tableView.reloadData()
}
}
Here I want to select row in a section, assume that Pizza section contains Margarita row and deep dish pizza as well contains same value. Here we need to select both rows which are different sections. It has to match with other section has same row or not whenever user tap on rows if match, all row has to select.
Store selected item names in an array and reload the tableview. In cellForRowAt method check if the array has the current item or not.
var selectedItems = [String]()
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
cell.textLabel?.text = items[indexPath.section][indexPath.row]
if selectedItems.contains(items[indexPath.section][indexPath.row]) {
print("Selected Item")
} else {
print("Item not selected")
}
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
if selectedItems.contains(items[indexPath.section][indexPath.row]) {
print("Selected Item")
selectedItems.removeAll { $0 == items[indexPath.section][indexPath.row]
} else {
print("Item not selected")
selectedItems.append(items[indexPath.section][indexPath.row])
}
tableView.reloadData()
}
I have a tableview. In the tableview cell I have a label and switch. Here I want to deselect the row when switch is off.
Here is my code:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! BM_MyBusinessTableViewCell
cell.tapSwitch.tag = indexPath.row
cell.businessLabel.text = labelArray[indexPath.row]
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
}
Don't select/deselect the cell when the switch is tapped. Just store the indexPath.row of the selected switches and reload the tableview.
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var tableView: UITableView!
let labelArray = ["Employees", "Break Time Setup", "Employee Timeoff", "Reports", "Messages"]
var selectedIndexPaths = [Int]()
override func viewDidLoad() {
super.viewDidLoad()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return labelArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") as! Cell
cell.selectionStyle = .none
cell.tapSwitch.isOn = selectedIndexPaths.contains(indexPath.row)
cell.tapSwitch.tag = indexPath.row
cell.tapSwitch.addTarget(self, action: #selector(tapSwitchAction(_:)), for: .valueChanged)
cell.businessLabel.text = labelArray[indexPath.row]
return cell
}
#objc func tapSwitchAction(_ sender: UISwitch) {
if sender.isOn {
selectedIndexPaths.append(sender.tag)
} else {
selectedIndexPaths.removeAll { $0 == sender.tag }
}
tableView.reloadData()
}
}
Then you can get the selected row values anywhere like this
#objc func getSelectedValues() {
let selectedLabelArray = labelArray.enumerated().filter { selectedIndexPaths.contains($0.offset) }
print(selectedLabelArray)
}
Update
Option 1
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if selectedIndexPaths.contains(indexPath.row) {
selectedIndexPaths.removeAll { $0 == indexPath.row }
} else {
selectedIndexPaths.append(indexPath.row)
}
tableView.reloadData()
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
//do nothing
}
Option 2
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let cell = tableView.cellForRow(at: indexPath) as? BM_MyBusinessTableViewCell {
cell.tapSwitch.isOn = !cell.tapSwitch.isOn
tapSwitchAction(cell.tapSwitch)
}
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
if let cell = tableView.cellForRow(at: indexPath) as? BM_MyBusinessTableViewCell {
cell.tapSwitch.isOn = !cell.tapSwitch.isOn
tapSwitchAction(cell.tapSwitch)
}
}
Im working on a project in swift 3 and it contains a UICollectionview inside a UItableViewCell. Therefore I have three Class in the order of UIViewController,UITableViewCell,UICollectionViewCell. The codes of the the classes as bellow.
UIViewController
extension ViewController : UITableViewDelegate, UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 5
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! TableViewCell
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 160
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return (section%2 == 0) ? "Games":"Love"
}
UITableViewCell
extension TableViewCell: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return imageNames.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewCell", for: indexPath) as! CollectionViewCell
cell.naeLabel.text = imageNames [indexPath.row]
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let clickedIndex = imageNames[indexPath.row]
print(clickedIndex)
}
}
My requirement is to pass an array from UIViewController to UITableViewCell. How could I achieve this?