How to process each cell in the collectionView

Asked 2 years ago, Updated 2 years ago, 41 views

I am currently creating a list application in the collection view.
I would like to display the information I entered in the transition destination (inputViewController) that I clicked on the cell in the collection view on the label on the cell, but I am troubled that the information I entered is reflected in all cells.
I would like to display the date and time obtained in the transition destination text field in the transition source (viewController).
The screen transition is segue on the storyboard, and it is connected from the collection view cell to the inputview controller.
If you know how to reflect each selected cell, I would appreciate it if you could let me know.

This is my first question, so please let me know if there are any unfamiliar points.

controller

class ViewController:UIViewController, UICollectionViewDataSource, UICollectionViewDelegate{
    // Collection view layout
    @IBOutlet weak var collectionView—UICollectionView!

    vartext1: String?

    let toDos=["1", "2", "3", "4", "5", "6" ]

    private entriesPerRow —CGFloat=3

    override func viewDidLoad(){
        super.viewDidLoad()

        let layout = UICollectionViewFlowLayout()
        layout.itemSize = CGSize (width: 100, height: 100)
        collectionView.collectionViewLayout=layout
    }

    functioncollectionView(_collectionView:UICollectionView, numberOfItemsInSection section:Int) - > Int {
        return 6 // Number of cells to display
    }

    functioncollectionView(_collectionView:UICollectionView, cellForItemAtindexPath:IndexPath)->UICollectionViewCell{

        letcell=collectionView.dueReusableCell(withReuseIdentifier: "Cell", for:indexPath)
        // Cell Color
        cell.backgroundColor=.white

        letlabel=cell.contentView.viewWithTag(2)as!UILabel
        label.text=toDos [indexPath.row]
        letdateLabel=cell.contentView.viewWithTag(3)as!UILabel
        // dateLabel.text=text1 [indexPath.row]
        dateLabel.text=text1

        return cell
    }

    functioncollectionView(_collectionView:UICollectionView, layoutCollectionViewLayout:UICollectionViewLayout,sizeForItemAtindexPath) ->CGSize{
        lethorizontalSpace —CGFloat=20
        let cellSize —CGFloat=self.view.bounds.width/3-horizontalSpace
        return CGSize (width:cellSize, height:cellSize)
    }
}
import UIKit

class inputViewController:UIViewController, UITextFieldDelegate{

    @IBOutlet weak var textField: UITextField!

    vardatePicker —UIDatePicker=UIDatePicker()

    override func viewDidLoad(){
        super.viewDidLoad()

        datePicker.datePickerMode=UIDatePicker.Mode.date
        datePicker.timeZone = NSTimeZone.local
        datePicker.locale=Locale(identifier: "ja")
        textField.inputView=datePicker


        let toolBar = UIToolbar (frame: CGRect (x:0, y:0, width:view.frame.size.width, height:35))
        letspaceItem = UIBarButtonItem(barButtonSystemItem:.flexibleSpace, target:self, action:#selector(done))
        let doneItem = UIBarButtonItem(barButtonSystemItem:.done, target:self, action:#selector(done)); toolBar.setItems([spaceItem, doneItem], animated:true)

        textField.inputView=datePicker
        textField.inputAccessoryView=toolBar

    }

    functextFieldShouldReturn(_textField:UITextField) - >Bool{

        textField.resignFirstResponder()

        return true
    }

    @ objc func done() {
        textField.endEditing(true)

        let formatter=DateFormatter()

        formatter.dateFormat="yyyy MM month dd day"

        textField.text="\"(formatter.string(from:datePicker.date))"
    }

    override funcdidReceiveMemoryWarning(){
        super.didReceiveMemoryWarning()
    }

    @ IBAction func finishButton(_sender:Any){

        let viewController: ViewController=
        self.storyboard?.instantiateViewController(withIdentifier: "finish") as!ViewController
        viewController.text1 = textField.text
        self.navigationController?pushViewController(viewController, animated:true)

    }

}

import UIKit

protocol inputViewControllerDelegate:class{
    func inputViewController(_inputVC:inputViewController, didFinishWithText:String?)
}

class inputViewController:UIViewController, UITextFieldDelegate{

    @IBOutlet weak var textField: UITextField!

    weak vardelegate: inputViewControllerDelegate?

    vardatePicker —UIDatePicker=UIDatePicker()


    override func viewDidLoad(){
        super.viewDidLoad()


        datePicker.datePickerMode=UIDatePicker.Mode.date
        datePicker.timeZone = NSTimeZone.local
        datePicker.locale=Locale(identifier: "ja")
        textField.inputView=datePicker


        let toolBar = UIToolbar (frame: CGRect (x:0, y:0, width:view.frame.size.width, height:35))
        letspaceItem = UIBarButtonItem(barButtonSystemItem:.flexibleSpace, target:self, action:#selector(done))
        let doneItem = UIBarButtonItem(barButtonSystemItem:.done, target:self, action:#selector(done)); toolBar.setItems([spaceItem, doneItem], animated:true)

        textField.inputView=datePicker
        textField.inputAccessoryView=toolBar


    }

    functextFieldShouldReturn(_textField:UITextField) - >Bool{

        textField.resignFirstResponder()

        return true
    }

    @ objc func done() {
        textField.endEditing(true)

        let formatter=DateFormatter()

        formatter.dateFormat="yyyy MM month dd day"

        textField.text="\"(formatter.string(from:datePicker.date))"


    }

    override funcdidReceiveMemoryWarning(){
        super.didReceiveMemoryWarning() 

    }




    @ IBAction func finishBtn(_sender:Any){


    delete?.inputViewController(self, didFinishWithText:textField.text)
        self.dismiss(animated:true, completion:nil)


}



}

swift ios xcode

2022-09-30 15:33

1 Answers

*You may have tried many things yourself, but the following explanation and code are written based on the code in the body of the question.It's a long sentence, but please keep that in mind until the end.

As you can see in the comments, text1 is not prepared for the number of cells, so in order to "reflect" each cell, different information should be prepared for each cell.

However, as mentioned in the comment, it is not recommended to have many separate arrangements.You should define the data type for cell display (although you can use it for more purposes).

Add the following data type declaration to the beginning of ViewController.swift:
(class ViewController... earlier.)

// Summarize items displayed in one cell into one data type
US>struct MyToItem{
    variable —String
    vardateString: String?// Name items that hold "Dates" so that they can be identified
    // Image, background color...
}

Actually, if you use the String type to represent the date, there are many difficulties, so I would like to change it to the Date type, but I will avoid fixing any parts that are not essential to solving the problem."Just ""reflecting for each selected cell"" still needs to be corrected…

"

Then, instead of the current text1 and toDos, keep the following array:

vartos: MyToItem = [
        MyTodoItem (title: "1", dateString:nil),
        MyTodoItem (title: "2", dateString:nil),
        MyTodoItem (title: "3", dateString:nil),
        MyTodoItem (title: "4", dateString:nil),
        MyTodoItem (title: "5", dateString:nil),
        MyTodoItem (title: "6", dateString:nil),
    ]

The collectionView(_:cellForItemAt:) implementation will be rewritten accordingly.

func collectionView(_collectionView:UICollectionView, cellForItemAtindexPath:IndexPath) ->UICollectionViewCell{

        letcell=collectionView.dueReusableCell(withReuseIdentifier: "Cell", for:indexPath)
        let item=todos[indexPath.row] // `item` is of type `MyTodoItem`
        // Cell Color
        // cell.backgroundColor=.white

        letlabel=cell.contentView.viewWithTag(2)as!UILabel
        label.text =item.title//<-
        letdateLabel=cell.contentView.viewWithTag(3)as!UILabel
        dateLabel.text=item.dateString//<-

        return cell
    }

So far, it's not a big deal, but I still have to think about just reflecting each cell.

    After editing the text with
  • InputViewController (although it may now be inputViewController, Swift has a very well-observed rule of "type names start with capital letters", use InputViewController to match the appropriate index)

  • todos After you rewrite the contents of

  • todos, you must call the reload method in UICollectionView to ensure that changes are reflected in the display

After editing the text in InputViewController (which may now be inputViewController, Swift has a very well-followed rule that says "type names start with uppercase letters", use InputViewController to unify the text.

After rewriting the contents of todos, you must call the reload method in UICollectionView to ensure that the changes are reflected in the display

Therefore, even if you make the array properly, InputViewController doesn't even know the index of the array...

Deligate patterns are often used to round this area to the original ViewController side.(If you use delete as a verb, it means "delegate" or "delegate", but in some situations, "round throw" is closer...)

Add the following protocols to InputViewController.swift:

protocol InputViewControllerDelegate:class{
    func inputViewController(_inputVC:InputViewController, didFinishWithText:String?)
}

(text does not reflect the date in the name, but it is too long...)

In InputViewController, add the delegate property of the above protocol type.

class InputViewController:UIViewController, UITextFieldDelegate{

    @IBOutlet weak var textField: UITextField!

    weak vardelegate —InputViewControllerDelegate?//<-

    vardatePicker —UIDatePicker=UIDatePicker()

    //...
}

After that, rewrite the finishButton(_:) process so that the process after the editing is complete is thrown round to this delegate.
ModifyThis part is the code for the Show as the segment type (Kind) and the InputViewController is also listed in UINavigationController.

@IBAction func finishButton(_sender:Any){
        delete?.inputViewController(self, didFinishWithText:textField.text)
        self.navigationController?popViewController(animated:true)
    }

(Based on the original code, I guess you are using the UINavigationController.If not, you have to change the "back to original screen" process, but never the code "show a new screen." If you are using UINavigationController, the "back" button will appear, but to simplify the conversation, I will cancel it and do nothing.)

If you choose Present Modally as the segment type and the InputViewController hides the UINavigationController, click here:

@IBAction func finishButton(_sender:Any){
        delete?.inputViewController(self, didFinishWithText:textField.text)
        // print(self.navigationController, self.presentingViewController)
        self.dismiss(animated:true, completion:nil)
    }

The length has been reasonably long with the explanation in between, but the editing results of InputViewController are not reflected in the original screen at all, because if you throw it round to delegate, there is no part to take on delegate.

  • Set delegate when transitioning to InputViewController

  • Implement the methods required for that delegate

delegate when transitioning to InputViewController

Implement the methods required for the delegate

You will need the part that says

The first point above is that the segment is drawn directly from the cell, so you will have to do it in the prepare(for:sender:) method.

Make sure that the identifier matches the following code (ToInput) to the appropriate segment.

The delegate role will be played by the ViewController, so the following will be added to the ViewController class:

varingItem:Int?//<- Add properties to remember the index of the element being edited

    override func prepare(for segment:UIStoryboardSegue, sender:Any?) {
        if segue.identifier == "ToInput" {
            // If you pull a segment from a cell, `sender` becomes a cell and this method is called
            letcell=sender as!UICollectionViewCell
            // Remember the index in the `todos` array of that cell (in instance properties) somewhere
            iflet indexPath=collectionView.indexPath(for:cell){
                editingItem=indexPath.item
            } else{
                editingItem=nil
            }
            // In this segment, the transition destination is 'InputViewController'
            let destVC=segue.destination as!InputViewController
            // Set that I (`self`) will take on the `delegate`
            destVC.delegate=self
        }
    }

At this rate, the last destVC.delegate=self should display an error, but since the ViewController does not conform to InputViewControllerDelegate, I will add this declaration after the class declaration.

extensionViewController:InputViewControllerDelegate{
    func inputViewController(_inputVC:InputViewController, didFinishWithText:String?){
        // If `self.editingItem` is left as nil, something is wrong, so check it just in case.
        iflettingItem=self.editingItem{
            // Update appropriate elements of `todos` array
            todos [editingItem].dateString=text
            // Ensure updates are reflected on the screen
            collectionView.reloadItems(at: [IndexPath(item:editingItem, section:0)])
        }
    }
}

Now, it's been a long time, but I think I've ignored most of the things that are necessary to "reflect on each selected cell."Please try and let me know if there is anything wrong or difficult to understand.


2022-09-30 15:33

If you have any answers or tips


© 2024 OneMinuteCode. All rights reserved.