I have a view controller with only one element inside - UICollectionView. This UIViewController is embedded in UINavigationController. In viewDidLoad method of this ViewController, I am initialising UISearchController and adding it to NavigationController. The problem is that whenever I try to search for something and I am currently entering the text in search bar, I need to tap 2 times on the UICollectionViewCell in order to trigger the tap. It looks like UISearchController is blocking the tap gesture of UICollectionViewCell. When I tap for the first time, the only thing that is happening is that "Cancel" button of the search bar is hiding. And then I need to tap another time to actually trigger the tap. I have tried many things to fix that, because I don't want the user having to tap 2 times on the cell instead of 1. These are the methods that I am calling in viewDidLoad method of this ViewController:
UISearchController initialisation:
searchController.searchResultsUpdater = self
navigationItem.hidesSearchBarWhenScrolling = false
navigationItem.searchController = searchController
definesPresentationContext = true
UICollectionView initialisation:
self.collectionView?.delegate = self
self.collectionView?.register(MyCell.self, forCellWithReuseIdentifier: MyCell.identifier)
self.collectionView?.contentInset = UIEdgeInsets(top: collectionViewVerticalInset, left: collectionViewHorizontalInset, bottom: collectionViewVerticalInset, right: collectionViewHorizontalInset)
UICollectionView layout initialisation:
let layout = UICollectionViewFlowLayout()
self.collectionView?.collectionViewLayout = layout
guard let collectionView = collectionView else { return }
UICollectionView data source initialisation:
dataSource = UICollectionViewDiffableDataSource<Section, MyEntity>(collectionView: collectionView) { (collectionView, indexPath, myEntity) -> UICollectionViewCell in
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: MyCell, for: indexPath) as! MyCell
cell.setupCell(myEntity: MyEntity)
return cell
}
I didn't changed anything in UICollectionView inside the storyboard. Also this ViewController is presented modally.
Related
I'm trying to create dynamic UICollectionView for my application using code below, the problem is that when i start scrolling from CollectionViewCell it's not scrolling but when i start scroll from outside it works fine.
let flowLayout = UICollectionViewFlowLayout()
flowLayout.scrollDirection = .horizontal
flowLayout.minimumInteritemSpacing = 10
flowLayout.minimumLineSpacing = 10
let collectionView = UICollectionView(frame: self.view.bounds, collectionViewLayout: flowLayout)
registerCell(collectionView: collectionView)
collectionView.delegate = self
collectionView.dataSource = self
collectionView.showsVerticalScrollIndicator = false
collectionView.showsHorizontalScrollIndicator = false
_scrollView.addSubview(collectionView)
You can Watch video
CollectionViewCell's properties
Make sure the cells don't have a custom touch-down event (e.g. tap gesture recognizer, or touchesBegan method without calling the super inside) and also make sure the collection view delays content touches, it's a common mistake to uncheck it and forget it.
I have a tableViewController that has three functions, two of which are working successfully.
Add cells by pressing button inside of toolbar = works
Being able to insert text into UITextView inside of cell, without the text duplicating and moving around = works
When button in cell is pressed, checkMarkImage appears, without getting duplicated when scrolling and moving around = does not work
The checkMarkImage does not stay put at the cell in which the button is pressed. It reappears when scrolling and sometimes disappears, despite my efforts of using a Boolean value, in order to track the cells of which have been checkmarked.
the configuration of the button inside of the my customCellClass:
#IBAction func checkMarkButton(_ sender: UIButton) {
if checkedOff{
UIImageView.animate(withDuration: 0.3, delay: 0, options: [], animations: {
self.checkMarkImage.alpha = 0.0
self.notesTextView.isEditable = true
self.notesTextView.textColor = .white
self.checkedOff = false
},completion: nil)
}
else{
UIImageView.animate(withDuration: 0.3, delay: 0, options: [], animations: {
self.checkMarkImage.alpha = 1.0
self.notesTextView.isEditable = false
self.notesTextView.textColor = .orange
self.checkedOff = true
}, completion: nil)
}
}
The handling of cell inside cellForRowAt:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier = "TableViewNotesCell"
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! TableViewNotesCell
cell.notesTextView.text = cellNumber[indexPath.row]
if cellNumber[indexPath.row] < " "{
print("celltext is less than nothing")
cell.notesTextView.textColor = .white
cell.notesTextView.isEditable = true
cell.checkMarkImage.alpha = 0.0
}
else{
if cell.checkedOff{
print("cell has been checked off")
cell.notesTextView.textColor = .orange
cell.notesTextView.isEditable = false
cell.checkMarkImage.alpha = 1.0
}
}
My expect the cell's checkMarkImage to stay at the cell in which the button is pressed, but the actual effect is that the checkMarkImage is re-occurring, when scrolling, and sometimes completely disappears
I am afraid that by doing this you not gonna achieve the desired outcome.
you must persist your data when you using UITableView or UICollectionView because these reuse the Cell when scrolling. so when you scroll the UITableView you are getting duplicate image or sometime loosing it. what you can do is :
Use an array of dictionary as dataSource to persist your data for the TableView.
you can also create your own model and use as the dataSource of TableView.
and if your data is large enough then you can go for CoreData / Sqllight.
What is the right approach to achieve what you see in the gif? It's a tableview with textfields. When you tap a textfield, a textview shows up with it's keypad. I have a tableview with a custom tableviewcell with a textfield(valueTextField). And in cellforrow i set the inputview of the textfield to a UITextView. When i press the textfield, the textview is supposed to show the toolbar on top (with a "Done" button) but no toolbar shows. When i tap the textview, still no toolbar.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cellId", for: indexPath) as! EditProfileTableViewCell
cell.isUserInteractionEnabled = true
cell.valueTextField.isUserInteractionEnabled = true
cell.valueTextField.delegate = self
toolBar = UIToolbar()
toolBar.sizeToFit()
let doneButton = UIBarButtonItem(title: "Done", style: UIBarButtonItemStyle.plain, target: self, action: #selector(editorDone))
toolBar.setItems([doneButton], animated: false)
editorTextView = UITextView(frame: CGRect(x:0, y:0, width:view.bounds.width, height:view.bounds.height))
cell.valueTextField.inputAccessoryView = toolBar
cell.valueTextField.inputView = editorTextView
return cell
}
The example you show is simply using a UINavigationController to navigate to a separate UIViewController when the cell is tapped. In this view controller, the UITextField will be handled something like this.
// View controller subclass conforming to UITextFieldDelegate protocol
class MyDetailViewController: UIViewController, UITextFieldDelegate {
// Set an IB Outlet to the text field in storyboard
#IBOutlet myTextField: UITextField!
override viewDidLoad() {
myTextField.delegate = self
// ToolBar
let toolBar = UIToolbar()
// Optional styling
toolBar.barStyle = .default
toolBar.isTranslucent = true
toolBar.tintColor = .black
toolBar.layer.borderWidth = 0
toolBar.sizeToFit()
// Buttons
let buttonOne = BarButtonItem(title: "One", style: .plain, target: self, action: #selector(yourSelectorOne(_:)))
let buttonTwo = BarButtonItem(title: "One", style: .plain, target: self, action: #selector(yourSelectorTwo(_:)))
let spacer = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
toolBar.setItems([buttonOne, spacer, buttonTwo], animated: false)
toolBar.isUserInteractionEnabled = true
textField.inputAccessoryView = toolBar
}
}
This will create two buttons, one on the left and one on the right of the toolbar. The type of keyboard can be set in Interface Builder.
To get this by tapping on the text field in the cell directly, without navigating to a new view controller, you could set your custom UITableViewCell subclass as the text field delegate.
Note that you will need to provide method stubs to conform to the UITextFieldDelegate protocol. These are not essential for this example so I have omitted them. XCode will offer to populate them for you.
There are few things in your code you want to change.
1) This view's frame causes that strange behavior:
UITextView(frame: CGRect(x:0, y:0, width:view.bounds.width, height:view.bounds.height))
2) The reason why you make this mistake is because you are confusing
.inputAccessoryView
.inputView
3) The gif you posted is based on navigationBar buttons. Basically, it by tapping on the cell it presents a detailedViewController with two items set in navigationBar
I have a text view inside my tableview cell and I use
self.tableView.beginUpdates()
self.tableView.endUpdates()
to update my cell height based on the amount of texts that user types and everything works pretty well except for the keyboard cover my text view area if my text view goes below the keyboard. I have tried
NSNotification.Name.UIKeyboardWillShow
to push my table view up and think it is the solution but the problem still exist and it really doesn't help at all in this case. Is there a way to keep the text always above the keyboard while typing? Thank you all for helping me and below is how I update my table view cell height
//set textView delegate in custom cell and use textView didchange func
func textViewDidChange(_ textView: UITextView) {
textDidChanges?()
}
//then in my cellForRowAt
cell.textDidChanges = {
self.tableView.beginUpdates()
self.tableView.endUpdates()
}
Inside the method to execute when a UIResponder.keyboardWillShowNotification is invoked,
get the keyboard's size and add it to the tableView and its scroll Indicator Insets.
NotificationCenter.default.addObserver(self, selector: #selector(VC.keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
func keyboardWillShow(_ notification: Notification) {
guard let info = notification.userInfo,
let keyboardFrameRect = info[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue
else { return }
let keyboardRect = keyboardFrameRect.cgRectValue
let contentInset = UIEdgeInsets(top: 0, left: 0, bottom: keyboardRect.height, right: 0)
tableView.contentInset = contentInset
tableView.scrollIndicatorInsets = contentInset
}
I have a ViewController who contains a TableView. I want in each cell, a red button who did something (let's say 'hello' for test), or when I touch the cell anywhere but not on the button I perform a segue.
But even when I touch the button, it's the segue who perform. I tried some search on SF but none of the questions help me...
I don't know if that's usual but when I touch the cell, all the row white background become gray, even the background of the red button.
In storyboard I have this cell hierarchy :
The user interaction is enabled on both cell and button.
In the questions I found they said, on the cell set 'accessory' on 'disclosure indicator', I did it, but if it's possible I would like to keep it at 'None'.
Also I set a custom class for the cell, here is the code :
class MyTableViewCell: UITableViewCell {
#IBOutlet weak var rentButton: UIButton? // I use this class for 2 tableview almost similar, but in the other I don't have the redButton, that's why I put an '?'
}
I also set the delegate and datasource of the tableView to my ViewController, here is the code of my view controller :
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! MyTableViewCell
cell.rentButton!.tag = indexPath.row
cell.rentButton!.addTarget(self, action: #selector(MyViewController.rent(_:)), forControlEvents: .TouchUpInside)
return cell
}
// Rent() is never called:
func rent(sender: UIButton) {
print("here \(sender.tag)")
}
EDIT : The solution was to put out the button from the conainer view !
You can stop the segue from actioning when the button is pressed by performing it programatically. You can do the following:
Remove your segue that goes from the cell to another view controller and change it go from the TableViewController to the other view controller
Create a variable in your TableViewController class such as: var buttonPressed = false
In your button action set buttonPressed to true
In didSelectRow, check that buttonPressed is not true then performSegue and change buttonPressed to false, else do nothing
As discussed, move the button from the container to the content view