Entire View stuck when UIButton is pressed - ios

I have a good amount of calculation codes get executed when the UIButton is pressed. But when the button is pressed, the whole view just get stuck and the button stay highlighted until the calculation finished.
For example, I set a loading view .hidden = false when the button is pressed too, but the loading view just doesn't show up during the calculation.
Is there any way to fix this?
Thanks in advance!

You should have to do the things in background from the updates you want to run on the UI:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
// do your task
dispatch_async(dispatch_get_main_queue()) {
// update some UI
}
}

Related

How to detect when the RPSystemBroadcastPickerView is tapped

I am using RPSystemBroadcastPickerView to start a system-wide screen recording from my app. The RPSystemBroadcastPickerView is completely autonomous in starting the recording and everything, which I guess makes sense - only user can start the screen recording by explicitly tapping the button.
I need to know when the RPSystemBroadcastPickerView is tapped. Right now the UI is showing keyboard, which I want to keep showing (it is a chat app). However, the form showing the list of broadcast extensions to pick one is being shown under the keyboard. See following image:
This effectively prevents the user to start the broadcast. If I knew when the user tapped RPSystemBroadcastPickerView, I could manually hide the keyboard at that moment. Any suggestions?
I did not find any callbacks for that, but you can create your transparent button, add it above the RPSystemBroadcastPickerView. When user will tap on your button you will be able to hide the keyboard and than transfer the action to the RPSystemBroadcastPickerView using the code:
for subview in screenSharingProviderPickerView.subviews {
if let button = subview as? UIButton {
button.sendActions(for: UIControlEvents.allTouchEvents)
}
}
I found similar solution to accepted answer, but you don't need to create transparent button.
I just add additional target-action pair to the picker button dispatch table, with #selector which will be fired, when button is pressed.
// picker is an instance of RPSystemBroadcastPickerView
for subview in picker.subviews {
if let button = subview as? UIButton {
button.addTarget(self, action: #selector(pickerAction), for: .touchUpInside)
}
}
// You can also accept _ sender: UIButton as parameter, if you need to.
#objc func pickerAction() {
// called when user press on RPSystemBroadcastPickerView
}
Note: Keep in mind that, if Apple change hierarchy of this view in the future, you probably need to update application as well.

How do I make changes to my interface while a button is pressed

I've got a button that will do some math when tapped.
Depending on the equation that the user enters, the button will be tapped, but then stay down, as the app takes a while to do the math in the button's function. It would be great if I could have a progress bar show the user during that time. I added one, but the problem is that it doesn't show up when the button has been tapped, as the button is lagging.. it seems that no changes can occur on the screen while the button is tapped down and lagging.
Sorry... I'm a bit of a noob... but I'm stuck...
You should do the math in a background thread, and all the UI related code in the main thread. For exemple, in Swift 3:
// Main thread by default:
// show progress bar here.
DispatchQueue.global(qos: .background).async {
// Background thread:
// start your heavy process here, for example:
for index in 1...1000 {
// do something in the loop
DispatchQueue.main.async {
// Main thread:
// update your progress bar here
}
}
DispatchQueue.main.async {
// Main thread, called after the previous code:
// hide your progress bar here
}
}
There is GCD. Here is a basic usage:
#IBAction func buttonPressed(sender: AnyObject)
{
// show progress bar
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
// start you main calculation
dispatch_async(dispatch_get_main_queue()) {
// hide your progress bar
}
}
}
dispatch_async function asynchronously runs the block of code on the given queue. In first dispatch_async call we dispatch the code to run on background queue. After we get result we update label on main queue with that result
I would add the progress bar on load but set it to invisible and then set it to visible as the first action of the button press method, Then do all of the math in the background.

Subview keeps moving when I push and pop navigation controller

image before pushing
This is what it's supposed to look like originally. The slider's added as a subview of the main view from Xib file on viewWillLoad, and I don't add it again if the subview exists on viewDidLoad. When I push, I call hidesBottomBarWhenPushed on the other view controller.
This is what I happens when I pop back:
image after popping
I have no idea why the subview does that.
Whatever you have set for bottom slider in viewDidLoad is the first time setting when view comes appear on screen. After that you have hide slider on push action, and goes to second view….right?
But when you come back to your view with pop, then how view can identify that it have show or hide that slider…? So, when you come back-I mean pop that time viewWillAppear is called. Put you code there…
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
// whatever you want to do, this is just for suggestion
if (bottomSlider.hidden == TRUE) {
bottomSlider.hidden = FALSE;
}
else {
bottomSlider.hidden = TRUE;
}
}

How to disable fast touches and double opening view?

Please help me with problem -
I have button, or label, or textfield at UiViewController view, which have method to open another view, if i touch it.
But if i touch button, label or textfield very fast, it can open two, or three, or more same views.
How to disable this opening? How to open only one view?
How i can do this for all project?
Thanks!
when you click on button then Calling method of the Button, Inside the button method you disable your button, and after when you dismiss open view then again you enable your button.
-(void)YourButtonMethod
{
YourBtn.enable = false;
}
// Enable your YourBtn when dismiss yourView.
It is usually easier to disable the UIButton when you enter the function and re-enable it before leaving. Would look something like this function
-(void)buttonTapped:(UIButton *)aButton
{
aButton.enabled = false;
// do your work here
// and before leaving
aButton.enabled = true;
}
But in your case you are pushing a UIViewController, so you should move the line aButton.enabled = true to your prepareForSegue function.

How to set the UIButton state to be highlighted after pressing it

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

Resources