I am having a hard time trying to replace UISlider on my app playing a invisible MPMoviePlayerController with UIProgressView.
UISlider is just create, very intuitive to change, Pretty much on the Update selector from MPMoviePlayerController I update its current status:
and previously I defined the min and max from the content of the MPMoviePlayerController:
-(void)UpdateTime:(NSTimer*)Timer
{
[self.sldProgress1 setValue:[[NSNumber numberWithDouble:playerSong.currentPlaybackTime] floatValue]];
}
UIProgressView doesn't provide similar approach, and I didn't find on the web a sample that matches what I am trying to do:
sldProgress2.maximumValue = [playerSong playableDuration];
sldProgress1.value = 0.0;
any suggestions?
UIProgressView doesn't have the concept of a user-settable max value - it's just 100.0. To make it work as you want, you'll have to do some math. Luckily, it's easy math:
CGFloat percentComplete = [playerSong.currentPlaybackTime floatValue] / [playerSong.playableDuration floatValue];
That will give you the percentage of the song that's complete, and you just update your UIProgressView to that number.
progressView.progress = percentComplete;
More or less - this code was mostly pseudo based on your question, but that's the idea.
You can keep using the UISlider and hide the thumbImage using this line:
[mySlider setThumbImage:[[UIImage alloc] init] forState:UIControlStateNormal];
This worked for me.
Related
I need to show a tilt of the device in my app. The algorithm is pretty simple, I'm using CMMotionManager's attitude for calculating tilt, and I'm updating a label which shows degrees like so:
- (void)tiltUpdated:(float)tilt
{
_degreesLabel.text = [NSString stringWithFormat:#"%.1f°", tilt];
}
My problem is next - CMMotionManager calls gyroscope updates approximately 10 times per second. And every time I'm calculating new tilt and calling tiltUpdated method each time. And when I do that, my app starts to incredibly lag. Few things I need to clarify:
Cause of lags is in updating the label. I defined it pretty easily
by commenting on it. So it's not the tilt calculations (that is why I
didn't provide a code for that here)
Applications also show camera output all the time. I turned off
camera and things got a little better but still, the application is lagging.
Is there any way to optimize updating UILabel text? Thanks in advance!
Ok, ending up answering my own question, but I hope that'll come in handy for somebody :).
I didn't find any valuable and detailed information about UILabel performance (which is too bad because I'm interested to learn something about that), but I found an alternative, which is CATextLayer.
I'm creating CATextLayer:
_textLayer = [CATextLayer new];
_textLayer.frame = CGRectMake(0.0f, _degreesLabelView.frame.size.height*0.5f - textHeight*0.5f, _degreesLabelView.frame.size.width, textHeight);
_textLayer.backgroundColor = [UIColor clearColor].CGColor;
_textLayer.foregroundColor = [UIColor whiteColor].CGColor;
_textLayer.alignmentMode = kCAAlignmentCenter;
_textLayer.contentsScale = UIScreen.mainScreen.scale;
_textLayer.fontSize = 17.0;
_textLayer.string = #"0°";
[_degreesLabelView.layer addSublayer:_textLayer];
And here I'm updating text:
- (void)tiltUpdated:(float)tilt
{
_textLayer.string = [NSString stringWithFormat:#"%.1f°", tilt];
}
Also CATextLayer is kind of "animating" text changes with slight "fadeIn/fadeOut" and I like it :)
My goal is to make an explosion-like animation where many particles are emitted in a short duration. My problem is that CAEmitterLayer, when it begins emitting, adds "future" particles to make it looks like the animation has been running for a while.
How can I disable or workaround this? When I increase the birthRate, I only want particles to start appearing from the emitterPosition, and not at all points along the CAEmitterCell's projected lifetime. Any help is appreciated.
#import "EmitterView.h"
#interface EmitterView ()
#property CAEmitterLayer* emitter;
#end
#implementation EmitterView
- (void) awakeFromNib {
[super awakeFromNib];
self.emitter = (CAEmitterLayer*)self.layer;
CAEmitterCell* snowflake = [CAEmitterCell emitterCell];
snowflake.contents = (id)[[UIImage imageNamed: #"snowflake"] CGImage];
snowflake.lifetime = 3;
snowflake.birthRate = 1;
snowflake.velocity = 50;
snowflake.emissionRange = 3.1415;
self.emitter.birthRate = 0;
self.emitter.emitterCells = [NSArray arrayWithObject: snowflake];
self.emitter.emitterPosition = CGPointMake(100, 100);
self.emitter.emitterSize = CGSizeMake(0, 0);
self.emitter.emitterShape = kCAEmitterLayerPoint;
}
+ (Class) layerClass {
return [CAEmitterLayer class];
}
- (void) burst {
self.emitter.birthRate = 10;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
self.emitter.birthRate = 0;
});
}
#end
This behavior actually changed with the release of iOS 7 and apparently hasn't gone back since then. It used to behave the way you would expect back before iOS 7, but either Apple introduced a bug that caused this, or they chose to change the behavior without telling anybody.
I filed a bug for this exact issue on August 28, 2013, which was closed as a duplicate of another bug report for the same issue. Apple's bug reporter is reporting that the other bug is still open, which means Apple hasn't addressed it yet, despite more than a year and a half to take care of it.
I recommend filing your own bug report with Apple, giving them a simple app that demonstrates the behavior, and maybe getting some renewed attention to it will help get them to do something about it.
EDIT:
After researching the issue a little, I found out that the solution is to add this line:
self.emitter.beginTime = CACurrentMediaTime();
It's important to know that CAEmitterLayer is a subclass of CALayer, which conforms to the CAMediaTiming protocol. Why this whole fact isn't better documented is beyond me.
Note that you probably want to call this from your - (void)burst method, but that if you call it again within a short period of time, while previous particles are still around, you might possibly see some odd behavior because of resetting the beginTime.
I am using MPMoviePlayerController to play audio and I want to show the buffered data on slider like this ...
I want to show the buffer data like red section in slider. I have tried to google it. But I didn't get any solution for it.
and How to make slider customize?
Thanks in advance...
Yes We can Show stream data using MPMoviePlayerController by its playableDuration. I am explaining all these steps involved me to make this custom_slider for my customize player...
(You Can direct read step 4 if you only interested in programming part)
These are the steps:
Step 1: (First Design part) i done it using standard Controls...
In Interface Builder place your UISlider immediately on top of your UIProgressView and make them the same size.
On a UISlider the background horizontal line is called the track, the trick is to make it invisible. We do this with a transparent PNG and the UISlider methods setMinimumTrackImage:forState: and setMaximumTrackImage:forState:.
In the viewDidLoad method of your view controller add:
[ProgressSlider setMinimumTrackImage:minImage forState:UIControlStateNormal];
[ProgressSlider setMaximumTrackImage:transparentImage forState:UIControlStateNormal];
[progressBar setTrackImage:maxImage];
[progressBar setProgressImage:streamImage];
where ProgressSlider refers to your UISlider and progressBar to your UIProgressView.
and you can make transparent image programmatically like this.
UIGraphicsBeginImageContextWithOptions((CGSize){512,5}, NO, 0.0f);
UIImage *transparentImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext();
you can change the size of it according to your progress view track image height width... I use {512,5} you can use {1,1} for default slider and progress view.
Reference taken form this answer https://stackoverflow.com/a/4570100/3933302
Step 2:
Now I tried to set the track and progress image of UIProgressView. But it was not showing the track and progress image. then i found this solution...to use this library available in github. https://gist.github.com/JohnEstropia/9482567
Now I changed occurrences of UIProgressView to JEProgressView, including those in NIBs and storyboards.
Basically, you'd need to force assigning the images directly to the UIProgressView's children UIImageViews.
The subclass is needed to override layoutSubviews, where you adjust the heights of the imageViews according to the image sizes.
Reference taken from this Answer https://stackoverflow.com/a/22322367/3933302
Step 3:
But now the question is from which will you make the image for slider and progressView. This is explained in this tutorial and You can download the psd also.
http://www.raywenderlich.com/32167/photoshop-tutorial-for-developers-creating-a-custom-uislider
Step 4:
Now come to programming part..
by using the playable duration it is possible to show stream data..(which was my biggest problem)
Using MPMovieLoadStatePlayable we can get when The buffer has enough data that playback can begin, but it may run out of data before playback finishes.
https://developer.apple.com/library/ios/documentation/MediaPlayer/Reference/MPMoviePlayerController_Class/index.html#//apple_ref/c/tdef/MPMovieLoadState
Now in your PlayAudio method...place this code..
-(void) playAudio:(NSURL *) url {
.............
streamTimer= [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:#selector(loadStateChange) userInfo:nil repeats:YES];
..........
}
and here is the definition of loadStateChange
-(void)loadStateChange
{
if(self.moviePlayer.loadState == MPMovieLoadStatePlayable){
[self.moviePlayer play];
slideTimer = [NSTimer scheduledTimerWithTimeInterval:0.5
target:self
selector:#selector(updateProgressUI)
userInfo:nil
repeats:YES];
[streamTimer invalidate];
}
else if(self.downloadList)
//if you are playing local file then it will show track with full red color
progressBar.progress= appDel.moviePlayerController.moviePlayer.duration;
}
and here is the definition of updateProgressUI
- (void)updateProgressUI{
if(self.moviePlayer.duration == self.moviePlayer.playableDuration){
// all done
[slideTimer invalidate];
}
progressBar.progress = self.moviePlayer.playableDuration/self.moviePlayer.duration ;
}
I hope this fully customize slider will help you lot in making custom player...I explained all my work in steps.....Thanks ....
You cannot do this with MPMoviePlayerController.
However if you switch to AVPlayer then you can use AVPlayerItem.loadedTimeRanges. It contains all information you need for visualization in your custom control.
There are number of questions that have been asked on that topic, for example:
AVPlayer streaming progress
According the documentation of MPMoviePlayerController (available here) you can use the playableDuration property to retrieve the duration that has been loaded by the player.
You would then have to subclass UISlider to draw the red part of your control in the drawRect: method.
As for AVPlayer you would call loadedTimeRanges (documentation here) on the AVPlayerItem representing your content. That would give you an array of (as of iOS 8) a single CMTimeRange representing the part of your media that has been buffered. I would advise using [NSArray firstObject] when retrieving the time range to protect your code against a possibly empty array.
I am trying to create a UISlider that has two pickers.I already have a UISlider which is working and functioning correctly.
Is there a way where I can add a picker to the UISlider that I already have or do I need to scrap everything that I have and programatically code it so that it has two pickers?
I would like the slider to have a minimum picker and a maximum picker. Im not sure if it is possible to add another picker to the slider i already have.
Here is the code i have for my current uislider.
- (IBAction)slider:(id)sender {
UISlider *slide = (UISlider *)sender;
CGFloat value = slide.value;
NSUInteger index = value * (priceDelegate.values.count - 1);
NSString *priceString = priceDelegate.values[index];
[self.tf_price setText:priceString];
_appDelegate.int_minPriceV = index;
}
If some one could help me I would be really grateful, thank you.
If it's a range slider you want then you're out of luck with the standard UISlider as it does not provide this kind of functionality.
You can subclass UISlider and do your own thing but we can all agree that that's somewhat a tedious job.
You can try these instead:
link1: https://github.com/muZZkat/NMRangeSlider
link2A: https://github.com/buildmobile/iosrangeslider
OR...
link2B: https://www.cocoacontrols.com/controls/rangeslider
If you're feeling a bit adventurous then follow this tutorial:
http://www.sitepoint.com/wicked-ios-range-slider-part-one/
How can I display a UIProgressView with a percentage?
As Vlad mentioned you can have a method which updates the value of progressView and its associated label. Call the following method when any update happens.
- (void)updateProgress
{
CGFloat stepSize = 0.1f;
[self.progressView setProgress:self.progressView.progress+stepSize];
self.progressLabel.text = [NSString stringWithFormat:#"Completed : %.0f",self.progressView.progress*100];
}
The progress indicator control does not offer the option of also displaying progress percent.
In order to achieve this, one solution would be to add a label positioned below / above your progress bar, and at each step when you refresh the progress on your progress indicator view, you also update the string value in on your label to display the percentage loaded.
There is one widget already available in iOS SDK - UIProgressView
Where you can set and get its property: progress
Hope this may help you.