0

So, I have a form to collect user data with UITextField. user can add up to 10 forms. so I thought of creating the form into a UICollectionView Cell.

my problem is within this form there is a delete button to remove the form if its not needed anymore. but this only works for the first time after that I would get an error

Fatal error: Index out of range

I am well aware of the error meaning but I have no clue how to track the row I want to delete specifically.

cell.deleteBtn.rx.controlEvent(.touchDown).subscribe(onNext: {_ in
           
            self.data = (self.viewModel?.form.value)!
            self.data.remove(at: row)
            self.viewModel?.form(self.data)
            self.contentViewHeightConstraints.constant -= CGFloat(779)
            

        }).disposed(by: self.disposeBag)

This is how I am deleting the form. ( I am also using RxSwift and this is the easiest way I could think of to delete with arrays).

I am still fairly new to Swift development so excuse any bad coding coming from me. please guide me through this.

Update:

so I changed the function into this :

cell.deleteBtn.rx.controlEvent(.touchDown).subscribe(onNext: {_ in
           
            self.data = (self.viewModel?.form.value)!
            self.data.remove(at: row)
            self.viewModel?.form(self.data)
            self.contentViewHeightConstraints.constant -= CGFloat(779)
            // I can't use index.row so I used row
            let indexPath = IndexPath(row: row, section: 0)
            
            self.collectionView.performBatchUpdates({
                self.collectionView.deleteItems(at: [indexPath])
            }){(finished) in
                self.collectionView.reloadItems(at: self.collectionView.indexPathsForVisibleItems)
                
            }

        }).disposed(by: self.disposeBag)

and now bam getting this error:

attempt to delete item 1 from section 0 which only contains 1 items before the update

Data source implementation:

self.viewModel!.form.asObservable().bind(to: self.formCV!.rx.items){
        tv,row,item in
            let cell = tv.dequeueReusableCell(withReuseIdentifier: "AddFormCell", for: IndexPath.init(row: row, section: 0)) as! AddFormCell
        
        cell.prepareForReuse()
        cell.initCellView()
        cell.iniStatesList()
        
        cell.formCountLbl.text! += " " + String(row + 1)
    
        if self.viewModel?.form.value.count ?? 0 > 1 {
            cell.deleteBtn.isHidden = false
        }else{
            cell.deleteBtn.isHidden = true
        }

adding a new form is like this:

@IBAction func addShop(){
    var arr = viewModel?.shops.value

    if(arr?.count ?? 0 < 4) {
        arr?.append(formViewModel(shopName: "", shopAddress: "", shopState: "", shopCity: "", shopPostCode: ""))
        
        viewModel?.form.accept(arr ?? [formViewModel(shopName: "", shopAddress: "", shopState: "", shopCity: "", shopPostCode: "")])
        
        self.contentViewHeightConstraints.constant += CGFloat(779)
    }else{
        self.openError()
        
    }

The self.data array is a global array defined to simply remove the forms cell from the ViewModel

Cell configuration:

func configCollectionView(){
    self.collectionView.register(UINib(nibName: addFormCell.identifier, bundle: .main), forCellWithReuseIdentifier: addFormCell.identifier)

    self.shopsCV.delegate = self
    
}

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    let size = Int((collectionView.bounds.width) / CGFloat(numberOfItemsPerRow))
    
    return CGSize(width: size, height: 779)
    
}

numberOfItemsPerRow = 1

  • @khaled-alsamman: Does this help https://stackoverflow.com/questions/16296351/how-to-delete-an-item-from-uicollectionview-with-indexpath-row ? – Sandeep Bhandari Oct 28 '21 at 05:22
  • @el-tomato: I think its collection boy or girl I guess :P as OP mentioned UICollectionView Cell in question – Sandeep Bhandari Oct 28 '21 at 05:23
  • Oops... Yes, it is. Thanks, @SandeepBhandari. – El Tomato Oct 28 '21 at 05:29
  • 1
    Thank you @SandeepBhandari I am looking at the link you just gave me. I will try something out now and hopefully it works – khaled alsamman Oct 28 '21 at 05:34
  • I am getting this error "attempt to delete item 1 from section 0 which only contains 1 items before the update" Also I couldn't use the indextPath.row so I used row straight away. maybe that is causing the issue. I am not sure why I can't use indexPath thou. – khaled alsamman Oct 28 '21 at 06:06
  • if I changed it into a tableView would that make things easier?? @SandeepBhandari – khaled alsamman Oct 28 '21 at 07:42
  • @khaled-alsamman: Can you add your data source implementation like number Of sections? number of Items in section etc etc seems like you deleted the only item in section 0 and yet you returned the section – Sandeep Bhandari Oct 28 '21 at 07:45
  • I will do so now, due to me using RxSwift, I can't really use delegate so I don't use datasource = self. I will cut out the code and add it now – khaled alsamman Oct 28 '21 at 07:46
  • @SandeepBhandari I have updated the code. if there is any better approach I can use please let me know how and I will do my best to implement it. – khaled alsamman Oct 28 '21 at 08:26

1 Answers1

0

so I managed to fix the issue by doing this:

cell.deleteBtn.rx.controlEvent(.touchDown).subscribe(onNext: {_ in
           
            
            let point = cell.deleteBtn.convert(CGPoint.zero, to: self.shopsCV)
            
            guard let indexPath = self.shopsCV.indexPathForItem(at: point)
            else{return}
            
            
            self.data = (self.viewModel?.shops.value)!
            self.data.remove(at: indexPath.row)
            self.viewModel?.shops.accept(self.data)
            
            self.shopsCV.performBatchUpdates({
            }){(finished) in
                
                self.shopsCV.reloadItems(at: self.shopsCV.indexPathsForVisibleItems)
                
            }

        }).disposed(by: cell.disposeBag)
       
            return cell

I had to use a disposeBag from within the cell in order to prevent it from clicking the delete btn twice or more. this works fine if the cells are in random and not in order.