Pozostawienie przycisku UIButton po dotknięciu

90

Gdy użytkownik kliknie przycisk, chciałbym, aby ten przycisk pozostawał wciśnięty przez cały czas wykonywania operacji sieciowych. Po zakończeniu operacji sieciowej chcę, aby przycisk powrócił do stanu domyślnego.

Próbowałem dzwonić - [UIButton setSelected:YES]zaraz po naciśnięciu przycisku (z odpowiednim wywołaniem - [UIButton setSelected:NO]po zakończeniu operacji w sieci), ale wydaje się, że nic nie daje. To samo, jeśli zadzwonię setHighlighted:.

Przypuszczam, że mógłbym spróbować zamienić obraz tła, aby wskazać wybrany stan na czas trwania operacji sieciowej, ale wygląda to na włamanie. Jakieś lepsze sugestie?

Oto jak wygląda mój kod:

- (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
źródło

Odpowiedzi:

73

Jak ustawiasz obrazy dla różnych UIControlStatesna przycisku? Czy ustawienie obrazu tła UIControlStateHighlighted, a także UIControlStateSelected?

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

Jeśli ustawiasz wybrany stan na zdarzeniu dotknięcia przycisku, a nie dotykasz w środku, twój przycisk będzie faktycznie podświetlony + wybrany, więc będziesz chciał to również ustawić.

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

Edytować:

Podsumowując moje uwagi w komentarzach i aby odnieść się do opublikowanego kodu ... musisz ustawić obrazy tła na pełny UIControlstan, w którym się znajdujesz. Zgodnie z Twoim fragmentem kodu ten stan kontroli byłby wyłączony + zaznaczony + podświetlony na czas działania sieci. Oznacza to, że musisz to zrobić:

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

Jeśli usuniesz highlighted = YES, będziesz potrzebować tego:

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

Masz zdjęcie?

Bryan Henry
źródło
W efekcie uważam, że robię to, co sugerujesz; Robię to tylko za pomocą Interface Builder. Dodałem kod, którego używam do mojego pytania powyżej ... może to rzuci trochę światła na problem? Chcę, aby przycisk pozostawał zaznaczony przez cały czas działania mojej sieci. Widzę, że przycisk podświetla się po dotknięciu, a podświetlenie znika po zakończeniu dotknięcia. Przycisk -nigdy- nie zostaje wybrany. Dlaczego to nie zadziała, nie ma dla mnie sensu, więc czuję, że robię coś głupiego. Sprawdziłem wszystkie moje połączenia IB i są dobre ...
Greg Maletic
Nie wierzę, że można ustawić obraz tła dla podświetlonego + wybranego stanu w Interface Builder, tylko wyróżnione i wybrane stany oddzielnie. Jeśli twój przycisk był w takim stanie, że podświetlony i wybrany były ustawione, uważam, że domyślnie będzie to twój normalny obraz, a nie żaden z podświetlonych lub wybranych obrazów. W swoim kodzie ustawiasz stan przycisku tak, aby zarówno zaznaczony, jak i podświetlony był TAK. Czy checkInButtonPushed jest połączony z „Touch Up Inside” czy z czymś innym?
Bryan Henry
Tak, jest podłączony do „Touch Up Inside”. A jeśli usunę kod związany ze stanem „wyróżniony”, nadal nie działa. Stan „wybrany” nigdy nie jest wyświetlany.
Greg Maletic
Poza tym, dlaczego wyłączasz przycisk? W rzeczywistości oznaczałoby to, że stan przycisku jest wyłączony + podświetlony + wybrany. Jeśli usunąłeś ustawienie linii podświetlone na YES, to wyłączyłeś + zaznaczone, więc musisz ustawić obraz dla stanu (UIControlStateDisabled | UIControlStateSelected).
Bryan Henry
OK, to był problem. Kiedy wyjąłem funkcję wyłączania, działało zgodnie z oczekiwaniami. Dzięki! (Odpowiadając na twoje pytanie, wyłączam przycisk, ponieważ nie chcę, aby ktokolwiek naciskał go ponownie podczas operacji sieciowych.)
Greg Maletic
30

Mam łatwiejszy sposób. Po prostu użyj "performSelector" z 0 opóźnieniem, aby wykonać [button setHighlighted:YES]. Spowoduje to ponowne podświetlenie po zakończeniu bieżącego runloop.

- (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
źródło
28

„Wszystko staje się lepsze, gdy włączysz zasilanie”

    button.selected = !button.selected;

działa idealnie ... po podłączeniu gniazdka do przycisku w Interface Builder.

Nie musisz ustawiaćBackgroundImage: forState :, kreator umożliwia określenie tła (w razie potrzeby zostanie zmieniony rozmiar) lub / i pierwszego planu (bez zmiany rozmiaru).

18446744073709551615
źródło
27

Spróbuj użyć NSOperationQueue, aby to osiągnąć. Wypróbuj kod w następujący sposób:

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

Mam nadzieję że to pomoże.

roberto.buratti
źródło
To świetne rozwiązanie, Roberto i Parth. Jednym drobnym szczegółem jest jednak to, że od czasu do czasu zobaczysz „migotanie”, jeśli użytkownik przytrzyma palec na przycisku. Czy jest jakiś sposób, aby zapobiec migotaniu?
Scott Lieberman
8

Użyj bloku, aby nie musieć budować całej oddzielnej metody:

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

Aktualizacja

Aby było jasne, nadal musisz ustawić tło (lub normalny obraz) dla stanów kombinacji, a także zwykłych, takich jak sbrocket w zaakceptowanej odpowiedzi. W pewnym momencie twój przycisk zostanie zarówno zaznaczony, jak i podświetlony, i nie będziesz mieć do tego obrazu, chyba że zrobisz coś takiego:

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

W przeciwnym razie twój przycisk może wrócić do UIControlStateNormal obrazu dla krótkiego wybranego + podświetlonego stanu, a zobaczysz flash.

Bob Spryn
źródło
FYI dispatch_get_current_queue()jest określane jako „niewiarygodne”.
Jacob Relkin,
1
Gdzie to jest powiedziane? Jaka jest alternatywa?
Bob Spryn,
Czy ma znaczenie używanie dispatch_afterw przeciwieństwie do dispatch_async?
Ilya
Tak. dispatch_asyncto lepszy wybór.
Bob Spryn
4

Szybko robię to jak poniżej.

Tworzę podklasę UIButtoni zaimplementowałem właściwość niestandardowąstate

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

}

U mnie działa idealnie.

Eike
źródło
1

Miałem podobny problem, gdy chciałem, aby przycisk pozostawał podświetlony po kliknięciu. Problem polega na tym, że jeśli spróbujesz użyć setHighlighted:YESwewnątrz akcji kliknięcia, zresetuje się ona natychmiast po kliknięciu akcji,- (IBAction)checkInButtonPushed

Rozwiązałem to używając NSTimer takiego jak ten

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

a następnie zadzwoń setHighlighted:YESz mojej selectCurrentIndexmetody. Używam zwykłych UIButtonTypeRoundedRectprzycisków.

fredrik
źródło
0

Mam inny sposób ... jeśli nie chcesz używać obrazów, a chcesz efektu wciśniętego przycisku, możesz podklasować Button i oto mój kod:

w pliku .h:

@interface reservasButton : UIButton {

BOOL isPressed;
}
 @end

W pliku .m:

#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
źródło
0

Oto implementacja C # / MonoTouch (Xamarin.iOS) przy użyciu metod przedstawionych powyżej. Zakłada się, że ustawiłeś już stan Highlighted image i konfiguruje wybrane i wybrane | wyróżnione stany tak, aby używały tego samego obrazu.

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
źródło