I made my own toolBar with a main action button on the middle of the bar.
When I click on a different button, my main action button image changes. I'd like to make it "obvious" by making an animation (the best would be something like UIImageView startAnimating()) but I fail at it.
Is there anything like the startAnimating() for the image inside UIButton to show the translation from an image to another ?
I dont wanna use sleep(1) or usleep(200) to block my app.
Any help or advice or suggestion would be appreciated.
What I have done in the past is flash a color on the view to give feedback to the user that what they have done was registered with the app. In your case you could flash a color or something in between the change in images.
I did this using a delay method I wrote which you can use by switching the color of the button, then delaying for about half a second or so, then show the next image. This will make it obvious that a change has been made. Here's the method to delay:
func runAfterDelay(delay: NSTimeInterval, block: dispatch_block_t) {
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay * Double(NSEC_PER_SEC)))
dispatch_after(time, dispatch_get_main_queue(), block)
}
You can put your image swap inside the block parameter. Hope this helps
Related
I have a QR Code Scanner view in which I have an AVCaptureVideoPreviewLayer and a UIButton.
When this view displays, the text within the button does not show. It should display the word 'Cancel'. If I touch the button, or swipe the button - but not tap it, the button text will show at that point.
Does anyone know how I can get the button text to display properly?
Here's what my view hierarchy looks like:
When I first enter the Scanner view, the buttons look like this:
There is no text. The text will show only after I touch the buttons:
One last thing, it seems this is an issue only is iOS 10...
Any suggestions welcome. Thanks!
Yes. You need to add an overlay view.
Main view containing video preview and overlay view.
Overlay view contains buttons.
For each button, you can use these events if you want to animate:
TouchDown
TouchUpInside
TouchOutInside
As the documentation of AVCaptureSession states, startRunning() call blocks the main thread so your UI is not drawn correctly.
The startRunning() method is a blocking call which can take some time,
therefore you should perform session setup on a serial queue so that
the main queue isn't blocked (which keeps the UI responsive). See
AVCam-iOS: Using AVFoundation to Capture Images and Movies for an
implementation example.
If you check Apple's example (Swift 3 and Objective-C), you can easily set a new queue for those actions without blocking your main thread.
This is the example for Swift 2.3 if you need it.
// Swift 2.3
private let sessionQueue = dispatch_queue_create("session queue", nil)
override func viewDidLoad() {
super.viewDidLoad()
dispatch_async(sessionQueue) {
self.configureSession()
}
dispatch_async(dispatch_get_main_queue()) {
self.setupScreen()
}
}
Use layer.insertSublayer(..., above: ...) or layer.insertSublayer(..., below: ...) to control your CALayers
Here's the similar answer link or
a bit more detailed instructions for Swift how to insert image on the top of video stream manual
I am developing an Apple Watch application in which I am calling an API which takes some time to execute, so to show user process is going on I need to show Activity Indicator. I am trying hard to find the solution as there is no Activity Indicator included in Apple Watch. One solution is to take image and animate image when we start the process and hide when we get response but is there any better solution as we need to add multiple images?
Unfortunately, there is no activity indicator interface element in WatchKit. You can create your own using the technique you've described. That is, unhide and begin an animated image sequence while you're waiting for a response, and hide it when the operation is complete.
This is the exact same technique that I use in my own Watch app. In my case, I load 4 images, so there are 4 unique activity indicator images that "spin" while the images are retrieved.
Create custom Activity Indicator using WKInterfaceImage like this;
IBOutlet WKInterfaceImage *animatedImage;
Now add images in sequence for animation in WKInterfaceImage in Images.xcassets
The method for hiding and unhiding activity indicator :
-(void)showActivityIndicator:(BOOL)yesOrNo
{
if (yesOrNo)
{
//unhide
[self.animatedImage setHidden:NO];
// Uses images in WatchKit app bundle.
[self.animatedImage setImageNamed:#"ActInd"];
[self.animatedImage startAnimating];
}
else
{
[self.animatedImage stopAnimating];
//hide
[self.animatedImage setHidden:YES];
}
}
We can use WKInterfaceImage set Images spinner#2x1.png and then animate image using startAnimatingWithImagesInRange
Now add images in sequence for animation in WKInterfaceImage in Images.x cassets
spinner#2x1.png,spinner#2x2.png.....spinner#2x40.png
Then use following method
NSRange model = NSMakeRange(0, 40);
/* set Image to ImageView */
[self.spinnerImage setImageNamed:#"spinner#2x1.png"];
/* set repeat count 0 for continues rotation */
[self.spinnerImage startAnimatingWithImagesInRange:model
duration:1.0
repeatCount:0];
This method works but it not smooth and if need set in center of view, then image needs to hidden first then unhide and animate image while process is going on and again hide after we get response,and if their multiple view then it becomes hectic is there any way to add image above all views or create dynamic activity indicator so that we add any where we need
'WatchKithas no UI control likeActivityIndicator, but we can create custom activityIndicator with the help ofWKInterfaceImage`.
You can find more more detail on this stackoverflow-link.
This question already has answers here:
iOS 7 launch image (splash screen) fades out
(7 answers)
Closed 7 years ago.
I want to hide the launch image immediately, instead of fading it out. Is there a way to do this?
You can make an initial view controller with a UIImageView that is identical to your launch image. Then you can specify whatever animation you want between the initial view controller and the first actual view controller in your app.
The answer is fairly straight-forward, as others have said. As long as the UIView behind the launch image is identical in appearance, all you have to do is delay your custom transition by 100 ms (an unnoticeable delay to the user) so that the launch image can fade to 0 alpha.
double delayInSeconds = 0.1;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(){
// Do custom launch image transition
});
This works seamlessly!
Create view controller that contains UIImageView and then just use an NSTimer then change to main controller.
The launch image display time is depended on your app's implement, simpler launch shorter time. I can't tell in the end what factors affect the time. But you can't hide it immediately by yourself, what you can do is just to show some other animations, which must be based on the launch image then do all kinds of launch animations. No matter what kind of animation to make, it will cast more launch time, but make the launch featured.
In my ViewController, I attempt to hide two images (currently displayed) at the same time, but after a delay of 3 seconds. I usr
[self performSelector:#selector(hideThem:) withObject:val afterDelay:3.0];
where "hideThem" is a routine that uses the following to hide the images. "val" is simply a NSNumber, not important to this question.
[image1 setHidden:YES];
[image2 setHidden:YES];
If I call "hideThem" directly (not using performSelector), both images disappear at the exact same time, which is the desired affect.
If, I use the performSelector, as shown, one image will hide, then after 0.5 seconds (or so) and the other image will hide. I do not have my own run loop. The images are UIImageView objects and are part of the view hierarchy under "self".
I assume this is an effect with how IOS handles timing of events, but I don't understand why the effect of the setHidden will occur with that 0.5 second delay when both should be set up as hidden "after" the performSelector call to "hideThem" fires.
What about IOS causes this behavior?
What are the recommended approaches to resolve this issue (so that, after 3 seconds, both images are hidden, visually, at the same time.
I can't figure out why such an issue would occur but I can give you an alternative using GCD:
double delayInSeconds = 3.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[self hideThem];
});
1)
I can see a colon : in method call #selector(hideThem:), double check whether your hideThem method takes input parameters.
2)
Make sure you call this [self performSelector:#selector(hideThem:) withObject:val afterDelay:3.0];
on main thread
try,
performSelectorOnMainThread
Ideas [I thought of 2 first but I suspect 1 will do the trick]:
1) Instead of setting their hidden property, use the UIView animation to turn both of their alphas to 0 over say a 100ms period, and in the completion block, set their hidden property to YES and reset their alphas back to 1.
2) Instead of hiding the two views immediately, create a new view to overlay them. You could even take a "snapshot" of what is under them and create an image out of that first:
create an image that you can use temporarily to overlay the two images you want to hide.
at the appropriate time, add that view to the hierarchy as the topmost view.
hide the two views under it
remove the temporary view
I found the culprit. Thanks for all the suggestions which helped me to isolate the issue. And, David H... very interesting ideas and I may actually have use for them as the project continues.
The answer is: after lots of searching, commenting out code, and tracking through things with logs I finally found ANOTHER related performSelector that was getting called elsewhere, with a timeframe of 2 seconds (hence, what I thought was showing a .5 sec delta between image hidings ).
In short... a strange coding bug.
Thanks again!
I am trying to set a custom UIView class's background color. The class also does quartz drawing in the drawRect:method.
Since background color change does not take place until the next redraw of the view, I change the UIView's backgroundColor property before calling setNeedsDisplay. I have set a UIActivityIndicatorView to animate while the view is redrawing.
self.backgroundColor = theColor;
[indicator startAnimating];
[self performSelectorInBackground:#selector(setNeedsDisplay) withObject:nil];
The indicator is stopped at the end of setNeedsDisplay. theColor will change every time I need to call this.
Let's say I have a time consuming setNeedsDisplay process. I would like to set the background and keep the indicator animation. Currently, changing backgroundColor calls setNeedsDisplay but doesn't even change the backgroundColor until the performSelectorInBackground method runs! Therefore my app hangs and no indicator is ever animated.
How do I deal with this ordering problem? Thanks.
Edit: I meant that my drawrect: may be time consuming.
Let's say I have a time consuming setNeedsDisplay process
Let's not. You have no business overriding setNeedsDisplay. I am not at all clear on what you're ultimately trying to accomplish but this entire question seems to be a misunderstanding of how to draw. When you call setNeedsDisplay (which, as you've been told, you must do in the main thread), that's that; you stand out of the way, and when the redraw moment comes, your view's drawRect: is called. That's drawing.
If the problem is simply that the activity indicator never gets going, that's because you never give it a chance. It too is not going to start going until the redraw moment. But you are stopping the activity indicator before the redraw moment even comes! So obviously you'll never see it go.
The way to start an activity indicator visibly before the next thing you do is to step out to the main thread after the next redraw moment. This is called "delayed performance". Example:
self.backgroundColor = theColor;
[indicator startAnimating];
double delayInSeconds = 0.1;
dispatch_time_t popTime =
dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
// do something further, e.g. call setNeedsDisplay
};
You could extend that example by calling dispatch_after yet again to stop the indicator after the next redraw moment.
I must impress upon you, however, that if the mere act of drawing takes so long that you need an activity indicator to cover it, you're drawing wrong. Your act of drawing must be very very fast. You might want to watch the WWDC 2012 video on this very topic; it gives excellent tips on how to draw efficiently.
You can update UI only on Main thread, not in backgroung
Try to use another subview with activity indicator, put in on before redraw and remove from superview after