Lassen Sie einen UIButton nach einer Berührung ausgewählt

90

Nachdem mein Benutzer auf eine Schaltfläche geklickt hat, möchte ich, dass diese Schaltfläche während der Ausführung eines Netzwerkvorgangs gedrückt bleibt. Wenn der Netzwerkvorgang abgeschlossen ist, soll die Schaltfläche in den Standardzustand zurückkehren.

Ich habe versucht [UIButton setSelected:YES]anzurufen - direkt nach dem Drücken der Taste (mit einem entsprechenden Anruf an - [UIButton setSelected:NO]nachdem mein Netzwerkbetrieb beendet ist), aber es scheint nichts zu tun. Das Gleiche, wenn ich anrufe setHighlighted:.

Ich nehme an, ich könnte versuchen, das Hintergrundbild auszutauschen, um einen ausgewählten Status für die Dauer der Netzwerkoperation anzuzeigen, aber das scheint ein Hack zu sein. Irgendwelche besseren Vorschläge?

So sieht mein Code aus:

- (IBAction)checkInButtonPushed
{
    self.checkInButton.enabled = NO;
    self.checkInButton.selected = YES;
    self.checkInButton.highlighted = YES;
    [self.checkInActivityIndicatorView startAnimating];
    [CheckInOperation startWithPlace:self.place delegate:self];
}

- (void)checkInCompletedWithNewFeedItem:(FeedItem*)newFeedItem wasNewPlace:(BOOL)newPlace possibleError:(NSError*)error;
{
    [self.checkInActivityIndicatorView stopAnimating];
    self.checkInButton.enabled = YES;
    self.checkInButton.selected = NO;
    self.checkInButton.highlighted = NO;
}
Greg Maletic
quelle

Antworten:

73

Wie stellen Sie die Bilder für die verschiedenen UIControlStatesauf der Schaltfläche ein? Stellen Sie ein Hintergrundbild für UIControlStateHighlightedsowie ein UIControlStateSelected?

UIImage *someImage = [UIImage imageNamed:@"SomeResource.png"];
[button setBackgroundImage:someImage forState:UIControlStateHighlighted];
[button setBackgroundImage:someImage forState:UIControlStateSelected];

Wenn Sie den ausgewählten Status für das Touchdown-Ereignis der Schaltfläche festlegen, anstatt ihn im Inneren zu verbessern, befindet sich Ihre Schaltfläche tatsächlich in einem hervorgehobenen + ausgewählten Status. Sie sollten dies also auch festlegen.

[button setBackgroundImage:someImage forState:(UIControlStateHighlighted|UIControlStateSelected)];

Bearbeiten:

Um meine Bemerkungen in den Kommentaren zusammenzufassen und den von Ihnen geposteten Code zu adressieren ... müssen Sie Ihre Hintergrundbilder auf den vollständigen UIControlStatus einstellen , in dem Sie sich befinden. Gemäß Ihrem Code-Snippet wäre dieser Kontrollstatus deaktiviert + ausgewählt + hervorgehoben für die Dauer des Netzwerkbetriebs. Dies bedeutet, dass Sie dies tun müssten:

[button setBackgroundImage:someImage forState:(UIControlStateDisabled|UIControlStateHighlighted|UIControlStateSelected)];

Wenn Sie das entfernen highlighted = YES, benötigen Sie Folgendes:

[button setBackgroundImage:someImage forState:(UIControlStateDisabled|UIControlStateSelected)];

Verstehe?

Bryan Henry
quelle
Tatsächlich glaube ich, dass ich tue, was Sie vorschlagen; Ich mache es nur über Interface Builder. Ich habe den Code, den ich verwende, zu meiner obigen Frage hinzugefügt ... Vielleicht würde das etwas Licht in das Problem bringen? Ich möchte, dass die Schaltfläche während der Dauer meines Netzwerkbetriebs ausgewählt bleibt. Was ich sehe, ist, dass die Schaltfläche beim Berühren hervorgehoben wird und die Hervorhebung verschwindet, wenn das Berühren abgeschlossen ist. Die Schaltfläche wird nie ausgewählt. Warum das nicht funktioniert, macht für mich keinen Sinn, deshalb habe ich das Gefühl, etwas Dummes zu tun. Ich habe alle meine IB-Verbindungen überprüft und sie sind gut ...
Greg Maletic
Ich glaube nicht, dass Sie im Interface Builder ein Hintergrundbild für den hervorgehobenen + ausgewählten Status festlegen können, sondern nur den hervorgehobenen und den ausgewählten Status separat. Wenn sich Ihre Schaltfläche in einem Zustand befindet, in dem sowohl hervorgehoben als auch ausgewählt festgelegt sind, wird standardmäßig Ihr normales Bild verwendet, nicht eines der hervorgehobenen oder ausgewählten Bilder. In Ihrem Code legen Sie den Status der Schaltfläche so fest, dass sowohl ausgewählt als auch hervorgehoben JA sind. Ist checkInButtonPushed mit "Touch Up Inside" oder etwas anderem verbunden?
Bryan Henry
Ja, es ist mit "Touch Up Inside" verbunden. Und wenn ich den Code entferne, der sich auf den Status "Hervorgehoben" bezieht, funktioniert er immer noch nicht. Der Status 'Ausgewählt' wird nie angezeigt.
Greg Maletic
Warum deaktivieren Sie die Schaltfläche? Das würde tatsächlich bedeuten, dass der Schaltflächenstatus deaktiviert + hervorgehoben + ausgewählt ist. Wenn Sie die auf JA hervorgehobene Linieneinstellung entfernt haben, haben Sie + ausgewählt deaktiviert, sodass Sie ein Bild für den Status (UIControlStateDisabled | UIControlStateSelected) festlegen müssen.
Bryan Henry
Okay, das war das Problem. Als ich die Deaktivierungsfunktion herausnahm, funktionierte sie wie erwartet. Vielen Dank! (Um Ihre Frage zu beantworten, deaktiviere ich die Schaltfläche, weil ich nicht möchte, dass jemand sie erneut drückt, während der Netzwerkbetrieb läuft.)
Greg Maletic
30

Ich habe einen einfacheren Weg. Verwenden Sie einfach "performSelector" mit 0 Verzögerung, um durchzuführen [button setHighlighted:YES]. Dadurch wird nach dem Ende des aktuellen Runloops eine erneute Hervorhebung durchgeführt.

- (IBAction)buttonSelected:(UIButton*)sender {
    NSLog(@"selected %@",sender.titleLabel.text);
    [self performSelector:@selector(doHighlight:) withObject:sender afterDelay:0];
}

- (void)doHighlight:(UIButton*)b {
    [b setHighlighted:YES];
}
Hlung
quelle
27

"Alles wird besser, wenn Sie das Gerät einschalten"

    button.selected = !button.selected;

funktioniert einwandfrei ... nachdem ich die Steckdose mit der Schaltfläche im Interface Builder verbunden habe.

Sie müssen SetBackgroundImage: forState: nicht festlegen. Mit dem Builder können Sie den Hintergrund (bei Bedarf wird die Größe geändert) oder / und den Vordergrund (ohne Größenänderung) angeben.

18446744073709551615
quelle
27

Versuchen Sie dies mit NSOperationQueue. Probieren Sie den Code wie folgt aus:

[[NSOperationQueue mainQueue] addOperationWithBlock:^{
    theButton.highlighted = YES;
}];

Hoffe das hilft.

roberto.buratti
quelle
Dies ist eine großartige Lösung, Roberto & Parth. Ein kleines Detail ist jedoch, dass Sie gelegentlich ein "Flackern" sehen, wenn der Benutzer seinen Finger auf die Schaltfläche gedrückt hält. Gibt es eine Möglichkeit, das Flackern zu verhindern?
Scott Lieberman
8

Verwenden Sie einen Block, damit Sie keine separate Methode erstellen müssen:

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0), dispatch_get_main_queue(), ^{
    theButton.highlighted = YES;
});

Aktualisieren

Um klar zu sein, müssen Sie immer noch den Hintergrund (oder das normale Bild) für die Kombinationszustände sowie die regulären festlegen, wie sbrocket in der akzeptierten Antwort sagt. Irgendwann wird Ihre Schaltfläche sowohl ausgewählt als auch hervorgehoben, und Sie haben kein Bild dafür, es sei denn, Sie tun Folgendes:

[button setBackgroundImage:someImage forState (UIControlStateHighlighted|UIControlStateSelected)];

Andernfalls kann Ihre Schaltfläche für den kurzen ausgewählten + hervorgehobenen Status auf das UIControlStateNormal-Bild zurückgreifen, und Sie sehen einen Blitz.

Bob Spryn
quelle
Zu Ihrer Information dispatch_get_current_queue()wird als "unzuverlässig" eingestuft.
Jacob Relkin
1
Wo steht das? Was ist die Alternative?
Bob Spryn
Gibt es eine Bedeutung im dispatch_afterGegensatz zu dispatch_async?
Ilya
Ja. dispatch_asyncist eine bessere Wahl.
Bob Spryn
3

In Kürze mache ich es wie folgt.

Ich erstelle eine Unterklasse von UIButtonund implementiere eine benutzerdefinierte Eigenschaftstate

class ActiveButton: UIButton {

    private var _active = false
    var active:Bool {
        set{
            _active = newValue
            updateState()
        }
        get{
            return _active
        }
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.addTarget(self, action: #selector(ActiveButton.touchUpInside(_:)), forControlEvents: .TouchUpInside)
    }

    func touchUpInside(sender:UIButton) {
        active = !active
    }

    private func updateState() {
        NSOperationQueue.mainQueue().addOperationWithBlock {
            self.highlighted = self.active
        }
    }

}

Funktioniert perfekt für mich.

Eike
quelle
1

Ich hatte ein ähnliches Problem, bei dem ich wollte, dass eine Schaltfläche nach dem Klicken die Hervorhebung beibehält. Das Problem ist, wenn Sie versuchen, setHighlighted:YESinnerhalb Ihrer Klickaktion zu verwenden, wird es direkt nach dem Klicken auf Aktion zurückgesetzt.- (IBAction)checkInButtonPushed

Ich habe dies mit einem solchen NSTimer gelöst

NSTimer *timer;
timer = [NSTimer scheduledTimerWithTimeInterval: 0.01
                                         target: self
                                       selector: @selector(selectCurrentIndex)
                                       userInfo: nil
                                        repeats: NO];

und dann setHighlighted:YESvon meiner selectCurrentIndexMethode aufrufen . Ich benutze normale UIButtonTypeRoundedRectKnöpfe.

fredrik
quelle
0

Ich habe einen anderen Weg ... Wenn Sie keine Bilder verwenden möchten und den Effekt eines gedrückten Knopfes wünschen, können Sie den Knopf unterordnen und hier ist mein Code:

in der .h-Datei:

@interface reservasButton : UIButton {

BOOL isPressed;
}
 @end

In der .m-Datei:

#import <QuartzCore/QuartzCore.h>


 @implementation reservasButton

 -(void)setupView {  //This is for Shadow

    self.layer.shadowColor = [UIColor blackColor].CGColor;
    self.layer.shadowOpacity = 0.5; 
    self.layer.shadowRadius = 1;    
    self.layer.shadowOffset = CGSizeMake(2.0f, 2.0f); //comment
    //   self.layer.borderWidth = 1;
    self.contentVerticalAlignment   = UIControlContentVerticalAlignmentCenter;
    self.contentHorizontalAlignment = UIControlContentHorizontalAlignmentCenter;

    // [self setBackgroundColor:[UIColor whiteColor]];

    //  self.opaque = YES;


}

-(id)initWithFrame:(CGRect)frame{
    if((self = [super initWithFrame:frame])){
        [self setupView];
    }

    return self;
}

-(id)initWithCoder:(NSCoder *)aDecoder{
    if((self = [super initWithCoder:aDecoder])){
        [self setupView];
    }

    return self;
}

//Here is the important code

 -(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{


  if (isPressed == FALSE) {

      self.contentEdgeInsets = UIEdgeInsetsMake(1.0,1.0,-1.0,-1.0);
      self.layer.shadowOffset = CGSizeMake(1.0f, 1.0f);
      self.layer.shadowOpacity = 0.8;

      [super touchesBegan:touches withEvent:event];

      isPressed = TRUE;

     }
     else {

         self.contentEdgeInsets = UIEdgeInsetsMake(0.0,0.0,0.0,0.0);
         self.layer.shadowOffset = CGSizeMake(2.0f, 2.0f);
         self.layer.shadowOpacity = 0.5;

         [super touchesEnded:touches withEvent:event];

         isPressed = FALSE;

     }


 } `
Pach
quelle
0

Hier ist eine C # / MonoTouch-Implementierung (Xamarin.iOS) unter Verwendung der oben dargestellten Ansätze. Es wird davon ausgegangen, dass Sie den Status des hervorgehobenen Bilds bereits festgelegt haben, und der ausgewählte und der ausgewählte | hervorgehobene Status werden so konfiguriert, dass dasselbe Bild verwendet wird.

var selected = button.BackgroundImageForState(UIControlState.Highlighted);
button.SetBackgroundImage(selected, UIControlState.Selected);
button.SetBackgroundImage(selected, UIControlState.Selected | UIControlState.Highlighted);
button.TouchUpInside += delegate
{
    NSTimer.CreateScheduledTimer(TimeSpan.FromMilliseconds(0), delegate
    {
        button.Highlighted = true;
        NSTimer.CreateScheduledTimer(TimeSpan.FromMilliseconds(200), delegate
        {
            button.Highlighted = false;
        });
    });
};
t9mike
quelle