Manter um botão UIB selecionado após um toque

90

Depois que meu usuário clicar em um botão, gostaria que ele permanecesse pressionado durante o tempo em que realizasse uma operação de rede. Quando a operação da rede for concluída, quero que o botão volte ao estado padrão.

Eu tentei chamar - [UIButton setSelected:YES]logo depois de apertar o botão (com uma chamada correspondente para - [UIButton setSelected:NO]depois que minha operação de rede terminar), mas não parece fazer nada. A mesma coisa se eu ligar setHighlighted:.

Suponho que poderia tentar trocar a imagem de fundo para denotar um estado selecionado durante a operação de rede, mas isso parece um hack. Alguma sugestão melhor?

Aqui está a aparência do meu código:

- (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
fonte

Respostas:

73

Como você está definindo as imagens para os diferentes UIControlStatesno botão? Você está definindo uma imagem de plano de fundo para UIControlStateHighlightedtambém UIControlStateSelected?

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

Se você estiver definindo o estado selecionado no evento de toque do botão em vez de retocar dentro, o botão estará realmente em um estado destacado + selecionado, então você também vai querer definir isso.

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

Editar:

Para resumir minhas observações nos comentários e abordar o código que você postou ... você precisa definir suas imagens de fundo para o UIControlestado completo em que você está. De acordo com o seu snippet de código, este estado de controle seria desabilitado + selecionado + destacado para a duração da operação da rede. Isso significa que você precisa fazer isso:

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

Se você remover o highlighted = YES, será necessário:

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

Veja a foto?

Bryan Henry
fonte
Na verdade, acredito que estou fazendo o que você sugere; Só estou fazendo isso por meio do Interface Builder. Eu adicionei o código que estou usando à minha pergunta acima ... talvez isso ajudasse a esclarecer o problema? O comportamento que desejo é que o botão permaneça selecionado durante toda a operação da minha rede. O que estou vendo é que o botão é realçado quando tocado e, em seguida, o realce desaparece quando o toque é concluído. O botão -nunca- é selecionado. Por que isso não funcionaria não faz sentido para mim, então eu sinto que estou fazendo algo estúpido. Eu verifiquei todas as minhas conexões IB e elas estão boas ...
Greg Maletic
Não acredito que você possa definir uma imagem de fundo para o estado destacado + selecionado no Interface Builder, apenas os estados destacado e selecionado separadamente. Se o seu botão estava em um estado em que realçado e selecionado foram definidos, acredito que ele seria o padrão para sua imagem normal, e não uma das imagens realçadas ou selecionadas. A partir do seu código, você está definindo o estado do botão de forma que tanto selecionado quanto realçado sejam SIM. O checkInButtonPushed está conectado a "Touch Up Inside" ou a alguma outra coisa?
Bryan Henry
Sim, está conectado ao "Touch Up Inside". E se eu remover o código relacionado ao estado 'destacado', ele ainda não funciona. O estado 'selecionado' nunca é mostrado.
Greg Maletic
Além disso, por que você está desativando o botão? Na verdade, isso significaria que o estado do botão está desativado + destacado + selecionado. Se você removeu a configuração de linha destacada para YES, então você desabilitou + selecionado, então você precisa definir uma imagem para o estado (UIControlStateDisabled | UIControlStateSelected).
Bryan Henry
Ok, esse era o problema. Quando retirei a funcionalidade de desativação, ela funcionou conforme o esperado. Obrigado! (Para responder à sua pergunta, estou desativando o botão porque não quero que ninguém o pressione novamente enquanto a operação de rede estiver em andamento.)
Greg Maletic
30

Eu tenho uma maneira mais fácil. Basta usar "performSelector" com 0 atraso para executar [button setHighlighted:YES]. Isso executará o realce novamente após o término do loop de execução atual.

- (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
fonte
28

"Tudo fica melhor quando você liga"

    button.selected = !button.selected;

funciona perfeitamente ... depois de conectar a tomada no botão no Interface Builder.

Você não precisa setBackgroundImage: forState :, o construtor permite que você especifique as imagens de fundo (será redimensionado se necessário) ou / e de primeiro plano (sem redimensionamento).

18446744073709551615
fonte
27

Tente usar NSOperationQueue para fazer isso. Experimente o código da seguinte forma:

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

Espero que isto ajude.

roberto.buratti
fonte
Esta é uma ótima solução, Roberto & Parth. Um pequeno detalhe, entretanto, é que você ocasionalmente verá uma "piscada" se o usuário mantiver o dedo pressionado sobre o botão. Existe alguma maneira de evitar a cintilação?
Scott Lieberman
8

Use um bloco para que você não precise construir um método totalmente separado:

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

Atualizar

Para ficar claro, você ainda precisa definir o fundo (ou imagem normal) para os estados de combinação, bem como os regulares, como sbrocket diz na resposta aceita. Em algum momento, seu botão estará selecionado e destacado, e você não terá uma imagem para isso, a menos que faça algo assim:

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

Caso contrário, o botão pode voltar para a imagem UIControlStateNormal para o breve estado selecionado + realçado e você verá um flash.

Bob Spryn
fonte
FYI dispatch_get_current_queue()é declarado como sendo "não confiável".
Jacob Relkin,
1
Onde isso está declarado? Qual é a alternativa?
Bob Spryn,
Há significado em usar dispatch_afterem oposição a dispatch_async?
Ilya
Sim. dispatch_asyncé a melhor escolha.
Bob Spryn
4

Rapidamente estou fazendo como o seguinte.

Eu crio uma subclasse de UIButtone implementei uma propriedade personalizadastate

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

}

Funciona perfeitamente para mim.

Eike
fonte
1

Tive um problema semelhante em que queria um botão para mantê-lo realçado após o clique. O problema é que se você tentar usar setHighlighted:YESdentro de sua ação de clique, ela será redefinida logo após você clicar em ação,- (IBAction)checkInButtonPushed

Resolvi isso usando um NSTimer como este

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

e depois chamar setHighlighted:YESdo meu selectCurrentIndexmétodo. Eu uso UIButtonTypeRoundedRectbotões normais .

Fredrik
fonte
0

Eu tenho outra maneira ... se você não quiser usar imagens, e quiser o efeito de um botão pressionado, você pode criar uma subclasse do botão e aqui está meu código:

no arquivo .h:

@interface reservasButton : UIButton {

BOOL isPressed;
}
 @end

No arquivo .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
fonte
0

Aqui está uma implementação C # / MonoTouch (Xamarin.iOS) usando as abordagens apresentadas acima. Ele assume que você já definiu o estado de imagem em destaque e configura os estados de | destaque selecionado e selecionado para usar a mesma imagem.

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
fonte