web-dev-qa-db-ja.com

iOS7でUIButtonがタップ時にハイライトを表示しない

私は同じようなことに関する大量の投稿を見てきましたが、どれもこの問題に完全に一致したり修正したりするものはありません。 iOS 7以降、UIButtonUITableViewCellに、またはフッタービューに追加するたびに「正常」に動作します。つまり、ターゲットアクションを受け取りますが、通常、UIButtonをタップすると発生します。 UIがファンキーに見えるようになり、ボタンがタッチに反応しなくなります。

私はこれがiOS7のバグとしてカウントされると確信していますが、誰かが解決策を見つけたか、解決策を見つけるのに役立ちます:)

編集:ボタンを長押しするとハイライト表示されることを忘れていましたが、標準ビューに追加しただけのようなクイックタップではありません。

コード:

ボタンの作成:

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);

私がテストしたもの:

//邪魔になった場合に備えて、UITableViewのジェスチャレコグナイザーを削除します。

for (UIGestureRecognizer *recognizer in self.tableView.gestureRecognizers) {
   recognizer.enabled = NO;
}

// Cellからジェスチャーを削除する

for (UIGestureRecognizer *recognizer in self.contentView.gestureRecognizers) {
       recognizer.enabled = NO;
    }

//これは少し軽いタッチを示していますが、これは望ましい外観ではありません

button.showsTouchWhenHighlighted = YES;
68
Eric

そのテーブルビューでは、このプロパティを追加するだけです。

tableview.delaysContentTouches = NO;

そして、コードの下に追加したばかりのセルを開始した後、cellForRowAtIndexPathを追加します。セルの構造は、iOS 6とiOS 7で明らかに異なります。
iOS 7では、UITableViewCellとコンテンツビューの間にUITableViewCellScrollViewコントロールが1つあります。

for (id obj in cell.subviews)
{
    if ([NSStringFromClass([obj class]) isEqualToString:@"UITableViewCellScrollView"])
    {
        UIScrollView *scroll = (UIScrollView *) obj;
        scroll.delaysContentTouches = NO;
        break;
    }
}
87
subramani

IOS 8以降、同じ手法をUITableViewサブビューに適用する必要があります(テーブルには非表示のUITableViewWrapperViewスクロールビューが含まれます)。 UITableViewCellサブビューを繰り返す必要はもうありません。

for (UIView *currentView in tableView.subviews) {
    if ([currentView isKindOfClass:[UIScrollView class]]) {
        ((UIScrollView *)currentView).delaysContentTouches = NO;
        break;
    }
}

この回答 はこの質問にリンクする必要があります。

40
Roman B.

私はこれを受け入れられた答えに加えようとしましたが、それは決して通りませんでした。これは、特定のクラスではなく、セレクターに応答するものを検索するため、セルのdelaysContentTouchesプロパティをオフにするより安全な方法です。

セル内:

for (id obj in self.subviews) {
     if ([obj respondsToSelector:@selector(setDelaysContentTouches:)]) {
          [obj setDelaysContentTouches:NO];
     }
}

TableViewで:

self.tableView.delaysContentTouches = NO;
27
Eric

IOS7とiOS8の両方で機能するソリューションの場合、カスタムUITableViewサブクラスとカスタムUITableViewCellサブクラスを作成します。

このサンプルUITableViewinitWithFrame:

- (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;
}

このサンプルUITableViewCellinitWithStyle: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;
}
18
Quentin

この問題を解決するために私がしたことは、次のコードを使用したUIButtonのカテゴリでした。

- (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; }];
}

uITableViewCellでボタンを押すと、ボタンは正しく反応し、UITableViewはdelaysContentTouchesが強制されていないため、正常に動作します。

16
Raphaël Pinto

Swift 2:

for view in tableView.subviews {
    if view is UIScrollView {
        (view as? UIScrollView)!.delaysContentTouches = false
        break
    }
}
10
brandonscript
    - (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;
        }
    }
}

enter image description here

5
Gank

UITableViewCell内のテキストのみのUIButtonがタッチ時に強調表示されないという同様の問題がありました。私が修正したのは、buttonTypeをCustomからSystemに戻すことでした。

DelaysContentTouchesをNOに設定すると、同じUITableViewCell内の画像のみのUIButtonのトリックが行われました。

self.tableView.delaysContentTouches = NO;

enter image description here

4
skg

これは、上記のRaphaëlPintoの回答のSwiftバージョンです。彼も賛成することを忘れないでください:)

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 }
}
4
Chris Harrison

Swiftのソリューション、iOS8のみ(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
    }

}

使用するには、ストーリーボードでクラスをNoDelayTableViewに変更するだけです。

IOS8では、セルのcontentView内に配置されたボタンがすぐに強調表示されるようになりました。

3
Chris

Chris Harrison's answer のわずかに変更されたバージョン。 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 }
        }
    }
}
2
SoftDesigner

受け入れられた答えは、私にとっていくつかの「タップ」では機能しませんでした。

最後に、Uibuttonカテゴリ(/サブクラス)に以下のコードを追加すると、100%動作します。

- (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];

}
1
rotoava

この問題の対処を簡単にするために、UITableViewCellにカテゴリ拡張を作成しました。これは、UITableViewCell contentViewから(下ではなく)ビュー階層を上るということを除いて、受け入れられた答えと基本的に同じです。

UITableViewに追加されたすべてのセルが、所有するdelaysContentTouchesUITableView状態に一致するようにdelaysContentTouches状態を設定する、完全に「自動」のソリューションを検討しました。これを機能させるには、UITableViewをスウィズルするか、開発者にUITableViewサブクラスの使用を要求する必要があります。どちらも要求したくないので、私はこの解決策に落ち着きました。

カテゴリ拡張とサンプルハーネスはこちら:

https://github.com/TomSwift/UITableViewCell-TS_delaysContentTouches

使用するのは非常に簡単です:

- (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;
}

カテゴリのコードは次のとおりです。

@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
0
TomSwift

Objcは動的で、scrollViewがdelaysContentTouchesに応答する唯一のクラスであるため、これはios 7と8の両方で機能するはずです(awakeFromNibのように、tableViewControllerの初期のどこかに置きます)。

for (id view in self.tableView.subviews)
    {
        if ([view respondsToSelector:@selector(delaysContentTouches)]) {
            UIScrollView *scrollView = (UIScrollView *)view;
            scrollView.delaysContentTouches = NO;
            break;
        }
}

また、viewController内のテーブルを選択して、ストーリーボードまたはペン先で「delaysContentTouches」をオフにする必要があります。ところで、viewController内でtableViewを使用している場合、これはiOS 7では動作しない可能性があります。少なくとも動作させることはできませんでした。

0
smileBot

Swift 3では、このUIView拡張機能はUITableViewCellで使用できます。できればcellForRowAtメソッドで使用してください。

func removeTouchDelayForSubviews() {
    for subview in subviews {
        if let scrollView = subview as? UIScrollView {
            scrollView.delaysContentTouches = false
        } else {
            subview.removeTouchDelayForSubviews()
        }
    }
}
0
Sunkas

私のためのそのソリューションは動作しません、私はfixed TableViewをサブクラス化し、これら2つのメソッドを実装します

- (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;
}
0
Serluca

Swift iOS 7および8の場合:

最初にユーティリティ関数を書きました:

class func classNameAsString(obj: AnyObject) -> String {
    return _stdlib_getDemangledTypeName(obj).componentsSeparatedByString(".").last!
}

次に、UITableViewをサブクラス化し、これを実装します。

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
        }
    }
}

また、UITableViewCellをサブクラス化し、これを実装します。

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
            }

        }
    }
}

私の場合、init(coder :)が実行されます。デバッグポイントをinit関数に入れて、どのinit関数が実行されるかを確認してから、上記のコードを使用して動作させてください。誰かを助けたい。

0
grandagile