Table with static cells won't scroll when I set delaysContentTouches = false - swift

The problem: HIGHLIGHT vs SCROLLING
My buttons inside the cell where not getting highlighted when I lightly tap on them. I had to tap hard and for a long time to be able to see the tap state of the button.
So I did this in order to set the delaysContentTouches to false (I didn't manage other way to do it) inside viewDidLoad():
for index in tableView.subviews {
if (index.isKindOfClass(UIScrollView)) {
let scrollViewFound = index as! UIScrollView
scrollViewFound.delegate = self
scrollViewFound.delaysContentTouches = false
scrollViewFound.canCancelContentTouches = true
scrollViewFound.scrollEnabled = true
}
}
This way the buttons highlight correctly but then I cannot scroll the table up or down, unless I start dragging from one of the empty cells --> userInteractionEnable = false in the empty cells
What I need:
To be able to highlight the buttons but also to scroll the table.
Is it even possible to have both, scrollable view and highlighted buttons?
What I have tried
I tried calling this function:
func touchesShouldCancelInContentView(view: UIView) -> Bool {
print("touchesShouldCancelInContentView happening---------")
return true
}
Which never gets called. I tried overriding But it gives an error:
Method does not override any method from its superclass
Which is weird, because UITableViewController inherits from UIScrollView. I also tried adding UIScrollViewDelegate to the class definition, but of course it gives another error that this is redundant.
Extra Information
The class is declared like this:
class Settings: UITableViewController, UITextFieldDelegate { ...
The tableView is made of Static Cells
The cells:
Some are empty: where UserInteractionEnable = false
Some have buttons with text field: I want these buttons to get highlighted. UserInteractionEnable = true. The button action is called by .TouchUpInside
Some have labels and a check image: Their action gets called in didSelectRowAtIndexPath which will change the labels colour and check images
Maybe it is relevant to say that when user clicks on any cell didSelectRowAtIndexPath it will
call a function to dismiss the keyboard.

You tried to subclass the wrong class, that's why it doesn't work. You have to subclass the UITableView class itself, and not the UITableViewController.
Can you try the following ?
- First
Subclass the TableView class in order to override the touchesShouldCancelInContentView function.
class UIDraggableTableView: UITableView {
override func touchesShouldCancelInContentView(view: UIView) -> Bool {
if (view.isKindOfClass(UIButton)) {
return true
}
return super.touchesShouldCancelInContentView(view)
}
}
- Second
In your TableViewController class, when viewDidLoad() is called, append the following right after super.viewDidLoad():
self.tableView = DraggableTableView()
This should solve your issue.
Part of this answer was taken from this StackOverflow post.

Related

How could I keep keyboard up when tableview cells are tapped?

I am a beginner learning Swift and trying to build a search page with Swift. In my search page of the app, I have added two Views in my storyboard with one View above the other.
The upper View contains a Collection View where I have two prototypes of collection view cells. The first type of the cells has Label. The second type of the cells has TextField.
The other View on the bottom half of the screen contains a dynamic Table View where I have a list of items that can be selected. Each row of the table view cells has a selection item.
So, when I tap on a table view cell, the selection item will appear in the collection view. If I type a keyword in the TextField in the collection view, table view reloads and shows all the selection items that has the keyword, so I can tap and add an item to the collection view.
I would like to keep adding by typing a keyword after I tap on a searched item in the table view. So, I made the first cell showing selected items with labels and the second cell that has the TextField separated into two sections of the collection view. So, I only reload the first section (without TextField) for each selection. But somehow the keyboard automatically resign whenever I tap on the table view cell to add an item to the collection view.
Is there any way I can keep the keyboard up even when I tap on the tableview cells?
The keyboard also resigns when I tap the collection view cells.
I would appreciate your advice. Thanks.
I hope you are having a good day.
You can try calling this method on the UITextField you would like to show the keyboard for (maybe call it after the user taps on the UITableViewCell):
textField.becomeFirstResponder()
where "textField" is the variable name of your UITextField.
Please let me know if this fixed your issue.
Edit #1
Hello! Since my previous solution did not achieve your intended behavior. There is another solution in my mind, however I have not tried it before.
As an introduction to the concept of delegation, there is a method created by Apple called "textFieldShouldEndEditing" which is called by Apple whenever any keyboard will disappear on any text field.
This method is created by Apple, but you can override it (i.e. customize it) to suit your needs and tailor its behavior.
To override this method you have to assign your class as the delegate of UITextField by adding UITextFieldDelegate to your class definition as follows:
class YourClassName: UIViewController, UITextFieldDelegate { }
Now you have to set your class as the delegate by saying textField.delegate = self For every UITextField you create in your collection views
You then can re-create the method we discussed earlier in your class:
func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
//let's implement it the next steps, but for now, let's return true.
return true
}
Now instead of Apple calling their version of the method, they will call yours.
You then can create a variable in the top level of your class (I will let you know where this will be helpful later), and "maybe" name it as:
var isCellBeingClicked = false
Now upon clicking on a cell, make this variable true, I believe you are using the method didSelectRowAt (but you could be using any other method which is fine):
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
[...]
isCellBeingClicked = true
[...]
}
Now back to our customized method textFieldShouldEndEditing mentioned in step 3. You can add this implementation:
func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
//If a cell is being clicked right now, please do not dismiss the keyboard.
if isCellBeingClicked {
isCellBeingClicked = false //reset the value otherwise the keyboard will always be there
return false
}
else { return true }
}
Please let me know if this fixes your issue.
Best regards

How to perform the instantiation of UISearchBar properly from a nested VC

I have the firstVC as 'homeController' which has an instance of UISearchBar and implements its delegate (UISearchBarDelegate), and doing this will instantiate the secondVC which is 'searchController' and has its delegate on another instance of UISearchBar as well, I need to fix that redundancy. I am using the homeController UISearchBarDelegate just because it let me instantiate the 'searchController'.
1: I'm using the method for the instantiation searchBarShouldBeginEditing on 'homeController' and perhaps it is wrong because the first second loads the color properly and then it changes to black.
2: searchBarTextDidBeginEditing loop itself. (from the color that I want to again the color that I want). -(infinite loop)
I can only use one at the time or searchBarShouldBeginEditing or searchBarTextDidBeginEditing. I would like to use the behavior of searchBarTextDidBeginEditing, but it reloads like crazy. Or the first second of searchBarShouldBeginEditing and know where does the application is in the state of that black screen because definitely, it is not the default color that I set, because it changes the color that I want on the first second and on the second second it is black. By the way, The application is not using the UISearchBar from the 'searchController', it is using the UISearchBar from 'homeController'. What is recommended?
Just right there I got two points.
1: Should I keep separated the logic as I am doing or should I mix it?
1.1: home and search have different cells, that's why I don't mix them. How can I use the instance of UISearchBar on 'searchController'?
2: If I keep it separated, should I use the UISearchBar from the 'searchController' or delete that instance and just use 'homeController'?
how to implement or instantiate properly the UISearchBar with the current scenario?
func searchBarShouldBeginEditing(_ searchBar: UISearchBar) -> Bool {
handleShowSearchVC()
return false
}
#objc func handleShowSearchVC() {
let modalUserSearchController = UserSearchController(collectionViewLayout: UICollectionViewFlowLayout())
modalUserSearchController.modalPresentationStyle = .overCurrentContext
navigationController?.pushViewController(modalUserSearchController, animated: true)
}
func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
handleShowSearchVC()
}

How to access an object inside a ViewController from within a TableViewCell

I am fairly new at coding in swift and I've been trying to find a solution to my problem for the past couple days, and am not able to.
I have a class named userData with various properties:
class UserData {
var name: String
var age: Int
var credits: Int = 1
...
init(name: String, age: Int) {
self.name = name
self.age = age
}
}
Inside the viewController.swift I have declared an object of this class:
...
var user = UserData(name: "testing", age: 5)
...
Inside the same viewController I have a UITableView with a few UITableViewCells. In every cell there is a UIStepper
Using the UIStepper from a cell I want to increment or decrement the property credits of my object user that sits inside the viewController.swift, and do that from within the tableViewCell.swift file
From what I can find, I think I should use a delegate but I can figure out how to implement it.
More information:
Pardon the art work, I am not an artist...
The user has a set amount of ants available to work (this number is a property of my object user). There is also a property for the amount of ants currently working ('ants used' in my screen shot).
At first, the white label on top says '0/5' (Meaning the user has 5 available ants to work but none are currently working).
When the user increments the stepper for 'scavenger ants', the white label on top should say '1/5' (Meaning that there is currently 1 ant working out of 5 that are available).
What I want to do, is that when the user clicks on a stepper, the user's property for the 'ants currently working' increments or decrements appropriately.
Thank you
Set that logic up in the view controllers' cellForRowAt.
When setting up the cell, you can add a function inside your view controller as the target every-time the UIStepper value changes:
// inside cellForRowAtIndexPath:
cell.addTarget(self, action: #selector(doSomething(sender:), for: .valueChanged)
Inside doSomething you set up the logic for updating your model.
func doSomething(sender: UIStepper) {
// do stuff here
}
Edit with an example of the delegate pattern which would be a better solution, for future readers.
First create a protocol:
protocol StepperCellDelegate {
func didChangeValueForStepper(inCell: Cell, whateverInfoYouWantHere:...)
func otherUsefulFunctions(...)
}
In your cell class, set a target/action for when your value is changed:
// inside the cell's initialization (`init(style:reuseIdentifier:)`
self.addTarget(self, action: #selector(valueChanged(sender:), for: .valueChanged)
Your cell also needs a delegate property:
weak var stepperCellDelegate: StepperCellDelegate?
doSomething would look something like this:
func valueChanged(sender: UIStepper) {
stepperCellDelegate?.didChangeValueForStepper(inCell: self, ...)
}
Your ViewController will implement the protocol
extension MyViewController: StepperCellDelegate {
func didChangeValueForStepper(inCell: Cell, whateverInfoYouWantHere:...) {
// implementation here
}
func otherUsefulFunctions(...){
// implementation here
}
}
And inside cellForRowAtIndexPath set itself as the delegate:
// inside cellForRowAtIndexPath:
cell.stepperCellDelegate = self
Delegation is the proper way of handling this.
That UItableviewCell — both appear to be similar —
should expose a protocol with a set of methods
that the UIViewController can implement. Methods are
triggered when you tap on the UIStepper widget.
Best to stick to this methodology, and reuse it everywhere. Makes
things manageable loosely coupled.

Add custom recognizer delay

I've disabled delaysContentTouches in my tableview subclass using:
delaysContentTouches = false
subviews.forEach { ($0 as? UIScrollView)?.delaysContentTouches = false }
But in one of my sections, I still want to keep the delay. Is there a way to cancel the delay for certain sections or perhaps I can add a custom recognizer delay to a section?
Sections are not actual objects within a tableView, so my answer to your first question is no. The .delaysContentTouches applies to the entire tableView.
For your second inquiry, I believe that one way it could be possible is through setting a delay for desired cells' scrollView subview. In your tableView(cellForRowAt: indexPath) func, you could have something like this:
if indexPath.section == 3 { //or whatever your desired section is
for view in cell.subviews {
if view is UIScrollView {
let currentView = view as! UIScrollView
currentView.delaysContentTouches = true
}
}
}
This will find the UIScrollView in your cell's subviews in your desired section. It will then set the .delaysContentTouches property accordingly.
I have not personally executed this code, just researched it, so let me know if it works.
Edit
Apparently the UIScrollView in UITableViewCell has been deprecated, so the above method will not work anymore.
My next best suggestion to you is to use a UILongPressGuestureRecognizer. This will not be quite the same thing as delaying the touch, but could have a similar effect in real execution.
You could use it in the same tableView(cellForRowAt: indexPath) func as so:
let press = UILongPressGestureRecognizer(target: self, action: #selector(handlePress))
press.minimumPressDuration = 2.0 //however long you want
cell.addGestureRecognizer(press)
Whatever you are trying to achieve by selecting certain rows of your tableView could be placed in the handlePress func above which would be trigged upon the long press.

YPDrawSignatureView - Table scrolling when drawing signature

So I want a signature view within a table cell. Obviously whenever somebody tries to draw in the cell, the table scrolls.
How would I stop the scrolling but ONLY when the user is writing in the signature box?
I found better solution for this issue rather than putting button. Implement the delegate methods in viewController,
class mainVC: UIViewController,YPSignatureDelegate {
Than set delegate of signature view to this view controller
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "SignatureCell", for: indexPath) as! SignatureCell
cell.signatureView.delegate = self
return cell
}
And then add these code. This are two delegates of YPSignature. Add in Main view controller
func didStart() {
tableView.isScrollEnabled = false
}
// didFinish() is called rigth after the last touch of a gesture is registered in the view.
// Can be used to enabe scrolling in a scroll view if it has previous been disabled.
func didFinish() {
tableView.isScrollEnabled = true
}
I would solve this with a button covering the cell, and when the user taps it, the cell displays the YPDrawSignatureView. Just before the signature view is shown, disable the scrolling:
tableView.scrollEnabled = false
Later when you save the signature, enable scrolling again by setting scrollEnabled to true.
I added a uitableview and custom cells. In one of the custom cells contain a button(ex. addSignatureButton) on the top of signatureView.
I used delegate method to communicate between uitableviewcell and uiviewcontroller. A delegate is added to UITableViewCell to notify whether the addSignatureButton is tapped. Once it is tapped, addSignatureButton is hidden, signatureView is visible and the tableview's scroll is disabled. When user finishes adding signature, signatureView is hidden, addSignatureButton is visible and tableview scroll is enabled.
https://github.com/alvinvgeorge/DrawSignatureOnTableViewCell