I'm working on a calculate app.
and has ton of button which I store in a big stackView (created in SB and no outlet).
each button cast some shadow (also set in SB attribute).
I want to get rid of shadow when button was pressed.
either tapGestureRecognizer or target action could only effect one UIButton.
any convenience way to acheive
PS I mean when button .touchupinside or tapGestureRecognizer .end .start when finger move button should still cast the shadow
help appreciated
UIButton will highlighted on click, so check button setting Change the title color in highlight state config to same as default state Or you can set:
[button setTitleColor:[UIColor blackColor] forState:UIControlStateHighlighted];
If you want to control Highlighted by code, you can disable normal highlighted by subclass Button and disable in touchesBegin:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
if (self.state == UIControlStateHighlighted) {
[self setHighlighted:NO];
}
}
You can assign the same IBAction to multiple buttons, so that the same method is called for all of them
func buttonDidTouch(sender:UIButton) {}
If now you want to identify which exactly button is being called, you can use the UIButton.tag property to identify it. The tag can be set in the SB for each button.
There are so many way to achieve the goal but I'm going with below.
Just give my logic.
Take one temporary optional variable of UIButton
Ex.
var myTemButton: UIButton()?
In the button action method
#IBAction func myButtonActionFunc(_ sender: UIButton) {
if myTemButton == nil {
myTemButton = sender
}
else {
// Use "myTemButton" and write here code for remove shadow Or other stuff that you want.
}
// use "sender" and write code here for apply shadow of button or other stuff that you want.
}
Related
How can I create one UIButton that allows me to perform 2 IBAction. For example after a user presses "Follow" the button turns to "Unfollow" and vice a versa. Is this best to accomplish with 2 buttons or 1 button and using different states. I've research how to acomplish this with one button but haven't found any solution. Thanks!
You can achieve that using a single button. You need to set the title of your buttons to each of it's states and toggle the states in the IBAction method when a selection or de-selection occurs:
[yourButton setTitle:#"Follow" forState:UIControlStateNormal];
[yourButton setTitle:#"Unfollow" forState:UIControlStateSelected];
And implement the IBAction method like:
- (IBAction)btnClicked:(UIButton *)sender
{
sender.selected = !sender.selected;
if (sender.selected)
{
// Currently selected the button and it's title is changed to unfollow
// Do your selection action here
}
else
{
// Currently de-selected the button and it's title is changed to follow
// Do your selection action here
}
}
Hi need to perform an action when user touched DOWN the UIButton
[button addTarget:self action:#selector(touchDown:event:) forControlEvents:UIControlEventTouchDown];
[button addTarget:self action:#selector(drag:event:) forControlEvents:UIControlEventTouchDragInside];
[button addTarget:self action:#selector(drag:event:) forControlEvents:UIControlEventTouchDragOutside];
[button addTarget:self action:#selector(touchUp:event:) forControlEvents:UIControlEventTouchUpInside];
[button addTarget:self action:#selector(touchUp:event:) forControlEvents:UIControlEventTouchUpOutside];
- (void)touchDown:(UIButton *)sender event:(UIEvent *)event {
//begin only called when I move my finger
}
- (void)drag:(UIButton *)sender event:(UIEvent *)event {
//called when I move my finger, after touchDown was called
}
- (void)touchUp:(UIButton *)sender event:(UIEvent *)event {
}
The root view controller of my app is tabbarviewcontroller, and each tab is a navigation view controller. in the viewWillAppear method of the chatting scene I hide the tab bar.
the result is, on device, when i touch down, it's not called, and when I move my finger a bit, it is called.
Note:
using Long Press gesture recognizer does not work either.
If I put the button away from tab bar area, it works on device.
on simulator, everything is fine.
I created a sample project:
https://drive.google.com/folderview?id=0B_9_90avvmZtRmRDeHFkbFJLaFk&usp=sharing
Is this button anywhere inside a scrollview (including collection or tableview)? If so, then that's exactly the expected behavior. The system waits to see if you're going to tap the button or drag the scrollview. From Apple:
Because a scroll view has no scroll bars, it must know whether a touch signals an intent to scroll versus an intent to track a subview in the content. To make this determination, it temporarily intercepts a touch-down event by starting a timer and, before the timer fires, seeing if the touching finger makes any movement. If the timer fires without a significant change in position, the scroll view sends tracking events to the touched subview of the content view. If the user then drags their finger far enough before the timer elapses, the scroll view cancels any tracking in the subview and performs the scrolling itself. Subclasses can override the touchesShouldBegin:withEvent:inContentView:, pagingEnabled, and touchesShouldCancelInContentView: methods (which are called by the scroll view) to affect how the scroll view handles scrolling gestures.
If you don't want this behavior, you can set parentview.delaysContentTouches to NO You may also need to set canCancelContentTouches to NO
Hi #OMGPOP I have the same problem with you, Now I has resolved it add below code:
self.navigationController.interactivePopGestureRecognizer.delaysTouchesBegan = NO;
I'm sure it's your savior, because I has download your sample code and add above code, It's work fine. like this:
self.navigationController.interactivePopGestureRecognizer.delaysTouchesBegan = NO;
[self.holdToRecordButton addTarget:self action:#selector(touchDown:event:) forControlEvents:UIControlEventTouchDown];
[self.holdToRecordButton addTarget:self action:#selector(drag:event:) forControlEvents:UIControlEventTouchDragInside];
[self.holdToRecordButton addTarget:self action:#selector(drag:event:) forControlEvents:UIControlEventTouchDragOutside];
[self.holdToRecordButton addTarget:self action:#selector(touchUp:event:) forControlEvents:UIControlEventTouchUpInside];
[self.holdToRecordButton addTarget:self action:#selector(touchUp:event:) forControlEvents:UIControlEventTouchUpOutside];
Do you have any subviews on your button? These might be intercepting the touches and lead to the behaviour you are seeing.
Do you have any UIGestureRecognizers on your button superview? They can cause such kind of delay.
I have met the same issue, and finally figured it out.
The issue here is that the button cannot receive action since its observer may be blacked out by the scroll view gesture recognisers.
What you need to do is to add a gesture on the container of the audio button, set gesture recogniser delegate to the view controller, and black out navigation controller popGestureRecognizer as below.
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch{
if ([UIDevice currentDevice].isIOS7 && touch.view == btnAudioRecorder)//issue is iOS7 only{
self.navigationController.interactivePopGestureRecognizer.enabled = NO;
}
return YES;
}
And check it out, the button can now answer your touch down event like a charm.
I had same problem and solved it.
In my case, there are two adjoining buttons. (Left button is buttonA. Right button is buttonB.)
ButtonA is not called touch down event. But ButtonB is called touch down event.
I tried overriding pointInside in ButtonB class.
override func pointInside(point: CGPoint, withEvent event: UIEvent?) -> Bool {
println(point)
}
Then, I tapped buttonA and called pointInside of ButtonB !!
point.x was negative value.
So I checked point and sendAction in ButtonB class.
override func pointInside(point: CGPoint, withEvent event: UIEvent?) -> Bool {
if CGRectContainsPoint(self.bounds, point) {
return true
}
// hoge.left means hoge.frame.origin.x
if point.x < 0, let buttonA = optionalButtonA {
buttonA.bounds.origin.x -= (self.left - buttonA.left) //self.left is buttonB.left
if CGRectContainsPoint(buttonA.bounds, point) {
buttonA.sendActionsForControlEvents(UIControlEvents.TouchDown)
return false
}
}
return false
}
I had the same issue. And tried all the answers post here, but none of them worked.
Fortunately, my colleague had the same issue, and he solved it!
The solution is:
put following code in your relative ViewController:
override var preferredScreenEdgesDeferringSystemGestures: UIRectEdge {
return .bottom
}
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?
.interactivePopGestureRecognizer?
.delaysTouchesBegan = false
}
The reason that touch down action was not called immediately is likely when your button is under the bottom of the screen, the touch down event will conflict with the iOS swipe up from bottom gesture. So it's delayed.
I have 4 buttons. When they are taped they each create a UIView underneath it, they expand under the buttons. I want the view to go back to its original when the buttons are taped a second time. How is this done?
Set an instance BOOL and flip it accordingly. In the IBAction the button is tied to check the BOOL and call the appropriate method to adjust the view.
You can use the selected property of your button, something like:
-(void)yourButtonIsTapped:(UIButton*)button {
if(button.selected) { //first time
//expand the view
button.selected = NO;
}
else { // second time
//hide view
button.selected = YES;
}
}
You can link the buttons from the IB th this method for the touchUpInside event but you will have to change the return type from void to IBAction.
And I think there are several other solutions for this case but this is the faster one and the easiest to explain.
Set every tag button to 0, change it to the opposite value when you press the button. Here is what you'll get:
First time that you press the button: button's tag = 0, expand the view and turn it to 1.
Second time you press the button: button's tag = 1, collapse it and turn it to 0.
So you haven't to create a bool for every button but you can use your button's tag variable.
Code should be like this.
- (void)handleButton:(UIButton *)button{
if(button.tag == 0) { <expandButton> }
else { <collapseButton> }
button.tag = !button.tag;
}
You can remove a view simply by calling
[viewUnderButton removeFromSuperview];
I hope I can ask this question clearly, I have a viewController that I build programmatically (not with NIB), this view controller has two buttons I draw on the lower portion of the view
"prev" and "next" what I'd like to do is, when I've reached the "end" I'd like to only draw the "prev" button, and not the "next", and vice-versa, when I'm at the beginning, I'd like to only draw the "next" and not the prev.
Any general ideas on how to approach this ?
Thanks,
uba
You can use "hidden" property of UIButton:
if (page == firstPage) {
self.myButtonPrev.hidden = YES;
} else {
self.myButtonPrev.hidden = NO;
}
if (page == lastPage) {
self.myButtonNext.hidden = YES;
} else {
self.myButtonNext.hidden = NO;
}
The first thing to do is to addTarget for your button to listen to touch events
[yourButtonNameHere addTarget:self action:#selector(yourCallBackFunctionNameHere:) forControlEvents:UIControlEventTouchUpInside];
What this does is whenever your button is pressed it calls the yourCallBackFunctionNameHere function. Do this for the other button too.
The semi colon after the function indicates that the function has to send the information of the UIElement that caused the event to occur.
Assign tags to both the buttons.
youButtonNameHere.tag=yourTag;
In the function check which button is sending the UIcontrolEvent by comparing the tags
- (void)yourFunctionNameHere:(id)sender {
UIButton *yourButton =(UIButton *)sender;
if(yourButton.tag==501){
// logic to check if this is the first or last page and act accordingly
yourButton.hidden = YES / NO based on what you want to do.
}else{
// logic to do otherwise.
}
I have a typical requirement wherein I need to keep a button in highlighted state after pressing it. I need to perform a task which should work only when a button is in highlighted state. Actually I am setting a button state to highlighted programatically.
[sender setHighlighted:YES];
And once the button is in highlighted state i need to perform another action.
- (IBAction)changeState: (UIButton*)sender
{
if (sender.highlighted == YES)
{
[self performSomeAtion:sender];
}
}
But, to my horror, whenever I press any button, the above condition is becoming true and the action is being performed repeatedly. Is there any way in which i can keep a UIButton's state to be highlighted after pressing it?
EDIT - Actually I need to perform 3 different actions for 3 different states of the button. I am already making use of selected state and normal state. Now, I need to make use of the highlighted state.
[sender setSelected:YES];
or you can simulate this effect with two image for your UIButton (notselectedimage.png and selectedimage.png), then keep track button state with a BOOL variable like BOOL buttonCurrentStatus;. Then in .h file:
BOOL buttonCurrentStatus;
and in .m file
// connect this method with Touchupinside function
- (IBAction)changeState:(UIButton*)sender
{
/* if we have multiple buttons, then we can
differentiate them by tag value of button.*/
// But note that you have to set the tag value before use this method.
if([sender tag] == yourButtontag){
if (buttonCurrentStatus == NO)
{
buttonCurrentStatus = YES;
[butt setImage: [UIImage imageNamed:#"selectedImage.png"] forState:UIControlStateNormal];
//[self performSomeAction:sender];
}
else
{
buttonCurrentStatus = NO;
[butt setImage:[UIImage imageNamed:#"notSelectedImage.png"] forState:UIControlStateNormal];
//[self performSomeAction:sender];
}
}
}
- (void)mybutton:(id)sender
{
UIButton *button = (UIButton *)sender;
button.selected = ![button isSelected]; // Important line
if (button.selected)
{
NSLog(#"Selected");
NSLog(#"%i",button.tag);
}
else
{
NSLog(#"Un Selected");
NSLog(#"%i",button.tag);
}
}
The highlighted state is used to highlight the button while it is being touched. A touch down event in the button highlights it. You should use the "selected" state instead.
If what you want to do is perform an action after the button is pressed, don't attach your method to the state change event, attach your method to the TouchUpInside event.
I just find a way, so I share it, just in case...
I kept my UIButton and set one image for each state (so you could go up to a 4 states button).
I set the UserInteractionEnabled to NO -> This button won't receive any touch.
The purpose of this first button is to show a state
I create a second custom UIButton with the same frame than the first one. For this one, none image will be set for the state (it's a fully transparent button). The purpose of this button is to catch the touch event. So I added a target to this button on the TouchUpInside event. And then when the event is fired, I change the state of the first button to Disabled, Highlighted, Selected, or none of these state (= Default state).
Everything is working like a charm!
The way you describe it, you'd be better off subclassing UIView to create your own three-state button.
Actually, you should even implement your own multistate buttonView, and manage the state it's in internally via an array of PNG for the looks and an array of states to know how many times it's been pressed.
Use [sender setSelected: YES];, I think it will be useful to you.
UIButton *btn_tmp=sender;
if(!(btn_tmp.selected))
{
[btn_temp setHighlighted:YES];
}
For iOS 7 only: you should consider setting the image renderMode to UIImageRenderingModeAlwaysTemplate. You can then use the tintColor to represent various states.
see How to apply a tintColor to a UIImage?
and
see Tint a UIView with all its subviews
The solution is tricky but it's possible.
The problem is that you tried to change the highlighted status in the button action method, which I suppose makes a clean up or check process at the end of the action and switch the highlighted status. When you try to debug it you get the highlighted = 1 but it will change at the end.
Strange but your "3 statuses button" is sometimes useful, when you'd like to keep a button in "highlighted" mode like the "selected" mode to get different action depending on the 3 statuses.
The only problem that you couldn't analyze this or switch it to highlighted mode in the button action method as this will switch to highlighted mode immediately as the user push it AND switch it back at the end.
The solution is using a dispatch.
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 0.1 * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[theButton setHighlighted:YES];
});
This will do the trick and you could use the 3 statuses.
According to apple, UIButton has a property of imageView:
Although this property is read-only, its own properties are read/write. Use these properties to configure the appearance and behavior of the button’s view
This means that you can set in the IB (in the storyboard) a picture for this button and set the highlighted picture:
Open the Attribute inspector.
Under Button section, choose an image.
In the same section, change the State Config to Highlighted. Notice the image you chose under default is now gone and now you can set a new picture for the Highlighted.
Now you have a button with 2 state config and all you have to do during runtime to change the button.highlighted = true. Also, check the UIControl under Configuring the Control’s Attributes for more states.
You can also do it programatically as follows:
Swift (and almost the same in Objective-C):
// Setting the highlighted image
self.someButton.imageView?.highlightedImage = UIImage(named: "imageNameFromImageAssest")
// someButton will now some the highlighted image and NOT the image set in the IB
self.someButton.imageView?.highlighted = true