J'ai écrit ma propre fonction pour faire défiler les champs de texte lorsque le clavier apparaît. Afin de fermer le clavier en appuyant loin du champ de texte, j'ai créé un UITapGestureRecognizer
qui se charge de renoncer au premier répondant sur le champ de texte lorsque vous appuyez loin.
Maintenant, j'ai également créé une saisie semi-automatique pour le champ de texte qui crée un UITableView
juste en dessous du champ de texte et le remplit d'éléments lorsque l'utilisateur saisit du texte.
Cependant, lors de la sélection d'une des entrées dans la table à complétion automatique, didSelectRowAtIndexPath
n'est pas appelé. Au lieu de cela, il semble que le dispositif de reconnaissance des gestes du robinet soit appelé et démissionne du premier répondant.
J'imagine qu'il y a un moyen de demander au programme de reconnaissance de gestes de tapotement de continuer à transmettre le message de tapotement au UITableView
, mais je ne peux pas comprendre de quoi il s'agit. Toute aide serait très appréciée.
Ok, finalement trouvé après quelques recherches dans la documentation de reconnaissance de geste.
La solution consistait à implémenter UIGestureRecognizerDelegate
et à ajouter ce qui suit:
////////////////////////////////////////////////////////////
// UIGestureRecognizerDelegate methods
#pragma mark UIGestureRecognizerDelegate methods
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
if ([touch.view isDescendantOfView:autocompleteTableView]) {
// Don't let selections of auto-complete entries fire the
// gesture recognizer
return NO;
}
return YES;
}
Cela s'en est occupé. J'espère que cela aidera les autres aussi.
Le moyen le plus simple de résoudre ce problème consiste à:
UITapGestureRecognizer *tapRec = [[UITapGestureRecognizer alloc]
initWithTarget:self action:@selector(tap:)];
[tapRec setCancelsTouchesInView:NO];
Ceci permet au UIGestureRecognizer
de reconnaître le tap et de transmettre le contact au répondant suivant. Une conséquence involontaire de cette méthode est si vous avez un UITableViewCell
à l'écran qui pousse un autre contrôleur de vue. Si l'utilisateur appuie sur la ligne pour fermer le clavier, le clavier et le Push seront reconnus. Je doute que ce soit ce que vous souhaitez, mais cette méthode convient à de nombreuses situations.
En outre, développant la réponse de Robert, si vous avez un pointeur sur la table en question, vous pouvez comparer directement sa classe au lieu de devoir convertir en chaîne et espérons que Apple ne change pas le nomenclature:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldReceiveTouch:(UITouch *)touch
{
if([touch.view class] == tableview.class){
return //YES/NO
}
return //YES/NO
}
N'oubliez pas que vous devez également déclarer le UIGestureRecognizer
pour qu'un délégué avec ce code s'y trouve.
Définissez cancelsTouchesInView
de votre outil de reconnaissance sur false. Sinon, il "consomme" le toucher pour lui-même et ne le transmet pas à la vue tableau. C'est pourquoi l'événement de sélection ne se produit jamais.
par exemple dans Swift
let tapOnScreen: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: "CheckTheTime")
tapOnScreen.cancelsTouchesInView = false
view.addGestureRecognizer(tapOnScreen)
Et pour Swift (basé sur la réponse de @Jason):
class MyAwesomeClass: UIViewController, UIGestureRecognizerDelegate
private var tap: UITapGestureRecognizer!
override func viewDidLoad() {
super.viewDidLoad()
self.tap = UITapGestureRecognizer(target: self, action: "viewTapped:")
self.tap.delegate = self
self.view.addGestureRecognizer(self.tap)
}
// UIGestureRecognizerDelegate method
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
if touch.view?.isDescendantOfView(self.tableView) == true {
return false
}
return true
}
J'ai peut-être une meilleure solution pour ajouter un geste de tapotement sur une vue sous forme de tableau, tout en permettant la sélection de cellule en même temps:
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
if gestureRecognizer is UITapGestureRecognizer {
let location = touch.locationInView(tableView)
return (tableView.indexPathForRowAtPoint(location) == nil)
}
return true
}
Je cherche juste une cellule au point de l'écran où l'utilisateur tape. Si aucun chemin d’index n’est trouvé, je laisse le geste recevoir le toucher, sinon je l’annule. Pour moi ça marche très bien.
Je pense qu'il n'est pas nécessaire d'écrire des blocs de codes, il suffit simplement de définir cancelsTouchesInView
sur false
pour votre objet de mouvement. Par défaut, il s'agit de true
et il vous suffit de le définir false
. Si vous utilisez UITapGesture
object dans votre code et utilisez également UIScrollView
(tableview, collectionview), définissez cette propriété false
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.dismissKeyboard))
tap.cancelsTouchesInView = false
view.addGestureRecognizer(tap)
Une solution similaire consiste à implémenter gestureRecognizer:shouldReceiveTouch:
en utilisant la classe de la vue pour déterminer quelle action entreprendre. Cette approche présente l'avantage de ne pas masquer les taps dans la région entourant directement la table (les vues de ces zones descendent toujours des instances UITableView, mais elles ne représentent pas des cellules).
Cela a aussi l'avantage de fonctionner avec plusieurs tables sur une seule vue (sans ajouter de code supplémentaire).
Avertissement: il est supposé que Apple ne changera pas le nom de la classe.
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
return ![NSStringFromClass([touch.view class]) isEqualToString:@"UITableViewCellContentView"];
}
Dans une situation différente, je souhaitais que la fonction de geste tactile soit appelée niquement lorsque l'utilisateur tapait sur à l'extérieur de la vue tabulaire. Si l'utilisateur a tapé dans la vue du tableau, la fonction de geste tactile ne devrait pas être appelée. En outre, si la fonction de geste tactile est appelée, elle doit toujours transmettre l'événement tactile à la vue sur laquelle vous avez appuyé sur le clavier plutôt que de le consommer.
Le code résultant est une combinaison de la réponse d'Abdulrahman Masoud et de la réponse de Nikolaj Nielsen.
extension MyViewController: UIGestureRecognizerDelegate {
func addGestureRecognizer() {
let tapOnScreen = UITapGestureRecognizer(target: self,
action: #selector(functionToCallWhenUserTapsOutsideOfTableView))
// stop the gesture recognizer from "consuming" the touch event,
// so that the touch event can reach other buttons on view.
tapOnScreen.cancelsTouchesInView = false
tapOnScreen.delegate = self
self.view.addGestureRecognizer(tapOnScreen)
}
// if your tap event is on the menu, don't run the touch event.
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
if touch.view?.isDescendant(of: self.tableView) == true {
return false
}
return true
}
@objc func functionToCallWhenUserTapsOutsideOfTableView() {
print("user tapped outside table view")
}
}
Et dans la classe MyViewController
, la classe qui a le UITableView
, dans le onViewDidLoad()
, je me suis assuré d’appeler addGestureRecognizer()
:
class MyViewController: UIViewController {
...
override func viewDidLoad() {
super.viewDidLoad()
...
self.addGestureRecognizer()
...
}
...
}
La solution simple consiste à utiliser des instances UIControl dans UITableViewCell pour obtenir des interventions. Vous pouvez ajouter des vues avec userInteractionEnables == NO à UIControl pour obtenir des taps.
Pour Swift 4.2, la solution consistait à implémenter UIGestureRecognizerDelegate
et à ajouter ce qui suit:
extension ViewController : UIGestureRecognizerDelegate {
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
if touch.view!.isDescendant(of: tblView) {
return false
}
return true
}
}
lorsque vous cliquez sur le mode table, cette méthode de délégation renvoie false et la méthode didSelectRowAtIndexPath
de la table fonctionne correctement.
Bien qu'il soit tard et que beaucoup de gens trouvent que les suggestions ci-dessus fonctionnent bien, je ne pouvais pas utiliser les méthodes de Jason ou TMilligan.
J'ai un Statique tableView avec plusieurs cellules contenant des textFields qui reçoivent des entrées Number en utilisant uniquement le clavier numérique. C'était idéal pour moi:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
if(![touch.view isKindOfClass:[UITableViewCell class]]){
[self.firstTF resignFirstResponder];
[self.secondTF resignFirstResponder];
[self.thirdTF resignFirstResponder];
[self.fourthTF resignFirstResponder];
NSLog(@"Touches Work ");
return NO;
}
return YES;
}
Assurez-vous que vous avez implémenté ce <UIGestureRecognizerDelegate>
dans votre fichier .h.
Cette ligne ![touch.view isKindOfClass:[UITableViewCell class]]
vérifie si une tableViewCell a été tapée et ferme tout clavier actif.
Voici ma solution, qui lie le récepteur shouldReceiveTouch du reconnaissant directement à l'affichage du clavier.
Dans votre délégué de reconnaissance de gestes tactiles:
#pragma mark - UIGestureRecognizerDelegate
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
if ([PFXKeyboardStateListener sharedInstance].visible) {
return YES;
}
return NO;
}
Et le PFXKeyboardStateListener.h
:
@interface PFXKeyboardStateListener : NSObject
{
BOOL _isVisible;
}
+ (PFXKeyboardStateListener *)sharedInstance;
@property (nonatomic, readonly, getter=isVisible) BOOL visible;
@end
Et le PFXKeyboardStateListener.m
:
static PFXKeyboardStateListener *sharedInstance;
@implementation PFXKeyboardStateListener
+ (PFXKeyboardStateListener *)sharedInstance
{
return sharedInstance;
}
+ (void)load
{
@autoreleasepool {
sharedInstance = [[self alloc] init];
}
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (BOOL)isVisible
{
return _isVisible;
}
- (void)didShow
{
_isVisible = YES;
}
- (void)didHide
{
_isVisible = NO;
}
- (id)init
{
if ((self = [super init])) {
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserver:self selector:@selector(didShow) name:UIKeyboardDidShowNotification object:nil];
[center addObserver:self selector:@selector(didHide) name:UIKeyboardWillHideNotification object:nil];
}
return self;
}
@end
Vous voudrez peut-être mettre à jour le modèle de singleton de l'auditeur de clavier, je ne l'ai pas encore fait. J'espère que cela fonctionne pour tout le monde aussi bien que pour moi. ^^
Implémentez cette méthode pour le délégué de UIGestureRecognizer:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldReceiveTouch:(UITouch *)touch
{
UIView *superview = touch.view;
do {
superview = superview.superview;
if ([superview isKindOfClass:[UITableViewCell class]])
return NO;
} while (superview && ![superview isKindOfClass:[UITableView class]]);
return superview != nil;
}
Dans Swift vous pouvez l'utiliser à l'intérieur
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if CheckTheTime() == true {
// do something
}else{
}
}
func CheckTheTime() -> Bool{
return true
}
Mon cas était différent, y compris uisearchbar et uitableview sur self.view. Je voulais écarter le clavier de uisearchbar en touchant sur la vue.
var tapGestureRecognizer:UITapGestureRecognizer?
override func viewWillAppear(animated: Bool) {
tapGestureRecognizer = UITapGestureRecognizer(target: self, action:Selector("handleTap:"))
}
Sur les méthodes de délégué UISearchBar:
func searchBarShouldBeginEditing(searchBar: UISearchBar) -> Bool {
view.addGestureRecognizer(tapGestureRecognizer!)
return true
}
func searchBarShouldEndEditing(searchBar: UISearchBar) -> Bool {
view.removeGestureRecognizer(tapGestureRecognizer!)
return true
}
Lorsque l'utilisateur touche à self.view:
func handleTap(recognizer: UITapGestureRecognizer) {
sampleSearchBar.endEditing(true)
sampleSearchBar.resignFirstResponder()
}