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.
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)
}
}
*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.
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 displayAfter 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.
© 2024 OneMinuteCode. All rights reserved.