Ich habe mir eine Menge Posts zu ähnlichen Dingen angeschaut, aber keines davon passt oder behebt dieses Problem. Seit iOS 7 funktioniert jedes Mal, wenn ich eine UIButton
zu einer UITableViewCell
oder sogar zur Fußansicht hinzufüge, "fein". Dies bedeutet, dass die Zielaktion empfangen wird. Es wird jedoch nicht das kleine Highlight angezeigt, das normalerweise beim Tippen auf eine UIButton
auftritt. Dadurch wirkt die Benutzeroberfläche funky, wenn die Schaltfläche nicht auf Berührung reagiert.
Ich bin mir ziemlich sicher, dass dies als Fehler in iOS7 zählt, aber jemand hat eine Lösung gefunden oder könnte mir helfen, eine zu finden :)
Edit: Ich habe vergessen zu erwähnen, dass es hervorgehoben wird, wenn ich die Taste lange gedrückt halte, nicht aber ein schnelles Tippen, wenn es nur zu einer Standardansicht hinzugefügt wird.
Code:
Schaltfläche erstellen:
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
button.titleLabel.font = [UIFont systemFontOfSize:14];
button.titleLabel.textColor = [UIColor blueColor];
[button setTitle:@"Testing" forState:UIControlStateNormal];
[button addTarget:self action:@selector(buttonPressed:) forControlEvents: UIControlEventTouchDown];
button.frame = CGRectMake(0, 0, self.view.frame.size.width/2, 40);
Dinge, die ich getestet habe:
// Gestenerkenner von UITableView
entfernen, falls sie im Weg sind.
for (UIGestureRecognizer *recognizer in self.tableView.gestureRecognizers) {
recognizer.enabled = NO;
}
// Entfernen von Gesten aus der Zelle
for (UIGestureRecognizer *recognizer in self.contentView.gestureRecognizers) {
recognizer.enabled = NO;
}
// Dies zeigt die kleine leichte Berührung, aber dies ist nicht das gewünschte Aussehen
button.showsTouchWhenHighlighted = YES;
In dieser Tabellenansicht fügen Sie einfach diese Eigenschaft hinzu.
tableview.delaysContentTouches = NO;
Fügen Sie in cellForRowAtIndexPath hinzu, nachdem Sie die Zelle initiiert haben, die Sie unter dem Code hinzufügen. Die Struktur der Zelle ist in iOS 6 und iOS 7 offensichtlich unterschiedlich.
iOS 7 haben wir ein Steuerelement UITableViewCellScrollView zwischen UITableViewCell und Inhaltsansicht.
for (id obj in cell.subviews)
{
if ([NSStringFromClass([obj class]) isEqualToString:@"UITableViewCellScrollView"])
{
UIScrollView *scroll = (UIScrollView *) obj;
scroll.delaysContentTouches = NO;
break;
}
}
Seit iOS 8 müssen wir dieselbe Technik auf UITableView-Subviews anwenden (Tabelle enthält eine verborgene UITableViewWrapperView-Bildlaufansicht). UITableViewCell-Unteransichten müssen nicht mehr wiederholt werden.
for (UIView *currentView in tableView.subviews) {
if ([currentView isKindOfClass:[UIScrollView class]]) {
((UIScrollView *)currentView).delaysContentTouches = NO;
break;
}
}
Diese Antwort sollte mit dieser Frage verknüpft werden.
Ich habe versucht, dies der akzeptierten Antwort hinzuzufügen, aber es ging nie durch. Dies ist eine viel sicherere Methode, um die DelaysContentTouches-Eigenschaft der Zellen zu deaktivieren, da sie nicht nach einer bestimmten Klasse sucht, sondern nach etwas, das auf den Selektor reagiert.
In der Zelle:
for (id obj in self.subviews) {
if ([obj respondsToSelector:@selector(setDelaysContentTouches:)]) {
[obj setDelaysContentTouches:NO];
}
}
In TableView:
self.tableView.delaysContentTouches = NO;
Erstellen Sie für eine Lösung, die unter iOS7 und iOS8 funktioniert, eine benutzerdefinierte Unterklasse UITableView
und eine benutzerdefinierte Unterklasse UITableViewCell
.
Verwenden Sie dieses Beispiel UITableView
s initWithFrame:
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
// iterate over all the UITableView's subviews
for (id view in self.subviews)
{
// looking for a UITableViewWrapperView
if ([NSStringFromClass([view class]) isEqualToString:@"UITableViewWrapperView"])
{
// this test is necessary for safety and because a "UITableViewWrapperView" is NOT a UIScrollView in iOS7
if([view isKindOfClass:[UIScrollView class]])
{
// turn OFF delaysContentTouches in the hidden subview
UIScrollView *scroll = (UIScrollView *) view;
scroll.delaysContentTouches = NO;
}
break;
}
}
}
return self;
}
Verwenden Sie dieses Beispiel UITableViewCell
s initWithStyle:reuseIdentifier:
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self)
{
// iterate over all the UITableViewCell's subviews
for (id view in self.subviews)
{
// looking for a UITableViewCellScrollView
if ([NSStringFromClass([view class]) isEqualToString:@"UITableViewCellScrollView"])
{
// this test is here for safety only, also there is no UITableViewCellScrollView in iOS8
if([view isKindOfClass:[UIScrollView class]])
{
// turn OFF delaysContentTouches in the hidden subview
UIScrollView *scroll = (UIScrollView *) view;
scroll.delaysContentTouches = NO;
}
break;
}
}
}
return self;
}
Was ich getan habe, um das Problem zu lösen, war eine UIButton-Kategorie, die den folgenden Code verwendet:
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesBegan:touches withEvent:event];
[NSOperationQueue.mainQueue addOperationWithBlock:^{ self.highlighted = YES; }];
}
- (void) touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesCancelled:touches withEvent:event];
[self performSelector:@selector(setDefault) withObject:nil afterDelay:0.1];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesEnded:touches withEvent:event];
[self performSelector:@selector(setDefault) withObject:nil afterDelay:0.1];
}
- (void)setDefault
{
[NSOperationQueue.mainQueue addOperationWithBlock:^{ self.highlighted = NO; }];
}
die Schaltfläche reagiert korrekt, wenn ich in einer UITableViewCell darauf drücke, und mein UITableView verhält sich normal, da die Variable delaysContentTouches
nicht erzwungen wird.
Hier ist die Antwort von Roman B. in Swift 2:
for view in tableView.subviews {
if view is UIScrollView {
(view as? UIScrollView)!.delaysContentTouches = false
break
}
}
- (void)viewDidLoad
{
[super viewDidLoad];
for (id view in self.tableView.subviews)
{
// looking for a UITableViewWrapperView
if ([NSStringFromClass([view class]) isEqualToString:@"UITableViewWrapperView"])
{
// this test is necessary for safety and because a "UITableViewWrapperView" is NOT a UIScrollView in iOS7
if([view isKindOfClass:[UIScrollView class]])
{
// turn OFF delaysContentTouches in the hidden subview
UIScrollView *scroll = (UIScrollView *) view;
scroll.delaysContentTouches = NO;
}
break;
}
}
}
Ich hatte ähnliche Probleme mit einem Nur-Text-UIButton in einer UITableViewCell, der bei Berührung nicht hervorgehoben wurde. Für mich war das Ändern des buttonType von Custom zurück in System.
Das Festlegen von delay_ContentTouches auf NO hat den Trick für das UIButton nur für Bilder in derselben UITableViewCell-Funktion bewirkt.
self.tableView.delaysContentTouches = NO;
Lösung in Swift, nur iOS8 (erfordert zusätzliche Arbeit an jeder Zelle für iOS7):
//
// NoDelayTableView.Swift
// DivineBiblePhone
//
// Created by Chris Hulbert on 30/03/2015.
// Copyright (c) 2015 Chris Hulbert. All rights reserved.
//
// This solves the delayed-tap issue on buttons on cells.
import UIKit
class NoDelayTableView: UITableView {
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
delaysContentTouches = false
// This solves the iOS8 delayed-tap issue.
// http://stackoverflow.com/questions/19256996/uibutton-not-showing-highlight-on-tap-in-ios7
for view in subviews {
if let scroll = view as? UIScrollView {
scroll.delaysContentTouches = false
}
}
}
override func touchesShouldCancelInContentView(view: UIView!) -> Bool {
// So that if you tap and drag, it cancels the tap.
return true
}
}
Zur Verwendung müssen Sie lediglich die Klasse in Ihrem Storyboard in NoDelayTableView
ändern.
Ich kann bestätigen, dass in iOS8 jetzt Schaltflächen, die in einer Inhaltsansicht in einer Zelle platziert sind, sofort hervorgehoben werden.
Dies ist eine Swift -Version von Raphaël Pintos Antwort oben. Vergiss nicht, ihn auch zu verteidigen :)
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
super.touchesBegan(touches, withEvent: event)
NSOperationQueue.mainQueue().addOperationWithBlock { () -> Void in self.highlighted = true }
}
override func touchesCancelled(touches: NSSet!, withEvent event: UIEvent!) {
super.touchesCancelled(touches, withEvent: event)
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(0.1 * Double(NSEC_PER_SEC)))
dispatch_after(time, dispatch_get_main_queue()) {
self.setDefault()
}
}
override func touchesEnded(touches: NSSet, withEvent event: UIEvent) {
super.touchesEnded(touches, withEvent: event)
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(0.1 * Double(NSEC_PER_SEC)))
dispatch_after(time, dispatch_get_main_queue()) {
self.setDefault()
}
}
func setDefault() {
NSOperationQueue.mainQueue().addOperationWithBlock { () -> Void in self.highlighted = false }
}
Leicht modifizierte Version von Chris Harrisons Antwort . Swift 2.3:
class HighlightButton: UIButton {
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
super.touchesBegan(touches, withEvent: event)
NSOperationQueue.mainQueue().addOperationWithBlock { _ in self.highlighted = true }
}
override func touchesCancelled(touches: Set<UITouch>?, withEvent event: UIEvent?) {
super.touchesCancelled(touches, withEvent: event)
setDefault()
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
super.touchesEnded(touches, withEvent: event)
setDefault()
}
private func setDefault() {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(0.1 * Double(NSEC_PER_SEC))), dispatch_get_main_queue()) {
NSOperationQueue.mainQueue().addOperationWithBlock { _ in self.highlighted = false }
}
}
}
Ich habe eine Kategorieerweiterung zu UITableViewCell
geschrieben, um das Problem einfach zu lösen. Es macht im Grunde dasselbe wie die akzeptierte Antwort, außer ich gehe die Ansichtshierarchie (im Gegensatz zu unten) vom UITableViewCell contentView
aus hoch.
Ich habe eine vollständig "automagische" Lösung in Betracht gezogen, bei der alle Zellen, die einer UITableView
hinzugefügt wurden, ihren delaysContentTouches
-Zustand so einstellen, dass sie dem UITableView
-Zustand des besitzenden delaysContentTouches
entsprechen. Um dies zu erreichen, muss ich entweder UITableView
wechseln oder vom Entwickler die Verwendung einer UITableView
-Unterklasse verlangen. Ich wollte es auch nicht. Ich entschied mich für diese Lösung, die meiner Meinung nach einfacher und flexibler ist.
Kategorienerweiterung und Musterkabelbaum hier:
https://github.com/TomSwift/UITableViewCell-TS_delaysContentTouches
Es ist kinderleicht zu benutzen:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// using static cells from storyboard...
UITableViewCell* cell = [super tableView: tableView cellForRowAtIndexPath: indexPath];
cell.ts_delaysContentTouches = NO;
cell.selectionStyle = UITableViewCellSelectionStyleNone;
return cell;
}
Hier ist der Code für die Kategorie:
@interface UITableViewCell (TS_delaysContentTouches)
@property (nonatomic, assign) BOOL ts_delaysContentTouches;
@end
@implementation UITableViewCell (TS_delaysContentTouches)
- (UIScrollView*) ts_scrollView
{
id sv = self.contentView.superview;
while ( ![sv isKindOfClass: [UIScrollView class]] && sv != self )
{
sv = [sv superview];
}
return sv == self ? nil : sv;
}
- (void) setTs_delaysContentTouches:(BOOL)delaysContentTouches
{
[self willChangeValueForKey: @"ts_delaysContentTouches"];
[[self ts_scrollView] setDelaysContentTouches: delaysContentTouches];
[self didChangeValueForKey: @"ts_delaysContentTouches"];
}
- (BOOL) ts_delaysContentTouches
{
return [[self ts_scrollView] delaysContentTouches];
}
@end
Da objc dynamisch ist und scrollView die einzige Klasse ist, die auf delay_ContentTouches reagiert, sollte dies für beide Betriebssysteme 7 und 8 funktionieren (setzen Sie es irgendwo zu Anfang in Ihren tableViewController, wie beispielsweise awakeFromNib):
for (id view in self.tableView.subviews)
{
if ([view respondsToSelector:@selector(delaysContentTouches)]) {
UIScrollView *scrollView = (UIScrollView *)view;
scrollView.delaysContentTouches = NO;
break;
}
}
Sie müssen möglicherweise auch "delayContentTouches" in Ihrem Storyboard oder in der Spitze deaktivieren, indem Sie die Tabelle in Ihrem viewController auswählen. Übrigens, das funktioniert unter ios 7 möglicherweise nicht, wenn Sie eine tableView in einem viewController verwenden, zumindest konnte ich es nicht zum Laufen bringen.
Die akzeptierte Antwort funktionierte bei einigen "Abgriffen" für mich nicht.
Schließlich füge ich den untenstehenden Code in einer Uibutton-Kategorie (/ Unterklasse) hinzu, und er funktioniert hundertprozentig.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
self.backgroundColor = [UIColor greenColor];
[UIView animateWithDuration:0.05 delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
self.backgroundColor = [UIColor clearColor];
} completion:^(BOOL finished)
{
}];
[super touchesBegan:touches withEvent:event];
}
In Swift 3 kann diese UIView-Erweiterung auf der UITableViewCell verwendet werden. Vorzugsweise in der cellForRowAt
-Methode.
func removeTouchDelayForSubviews() {
for subview in subviews {
if let scrollView = subview as? UIScrollView {
scrollView.delaysContentTouches = false
} else {
subview.removeTouchDelayForSubviews()
}
}
}
Diese Lösung funktioniert für mich nicht, ich fixed Unterklasse TableView und implementiere diese beiden Methoden
- (instancetype)initWithCoder:(NSCoder *)coder{
self = [super initWithCoder:coder];
if (self) {
for (id obj in self.subviews) {
if ([obj respondsToSelector:@selector(setDelaysContentTouches:)]){
[obj performSelector:@selector(setDelaysContentTouches:) withObject:NO];
}
}
}
return self;
}
- (BOOL)delaysContentTouches{
return NO;
}
Lösung in Swift für iOS 7 und 8:
Zuerst habe ich eine Utility-Funktion geschrieben:
class func classNameAsString(obj: AnyObject) -> String {
return _stdlib_getDemangledTypeName(obj).componentsSeparatedByString(".").last!
}
dann Unterklasse UITableView und implementiere dies:
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
for view in self.subviews {
if (Utility.classNameAsString(view) == "UITableViewWrapperView") {
if view.isKindOfClass(UIScrollView) {
var scroll = (view as UIScrollView)
scroll.delaysContentTouches = false
}
break
}
}
}
Ich habe auch die Unterklasse UITableViewCell und implementiere diese:
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
for view in self.subviews {
if (Utility.classNameAsString(view) == "UITableViewCellScrollView") {
if view.isKindOfClass(UIScrollView) {
var scroll = (view as UIScrollView)
scroll.delaysContentTouches = false
}
}
}
}
In meinem Fall läuft die Init (Coder :). Bitte geben Sie in Ihren init-Funktionen einen Debug-Punkt an, um zu wissen, welche init-Funktion ausgeführt wird. Verwenden Sie dann den obigen Code, damit es funktioniert.