In my iPhone app I am downloading some data from an FTP server. To show the action I am using UIActivityIndicator. If I put UIProgressView there instead of UIActivityIndicator, it will be more appropriate. How do I use UIProgressView while downloading some data? Can anybody give me a tutorial link or example code? Thanks in advance.
first you create IBOutlet in .h file
IBOutlet UIProgressView * threadProgressView;
Then in .m file in viewdidload first set progress to 0.0 and then call makeMyProgressMoving method
threadProgressView.progress = 0.0;
[self performSelectorOnMainThread:#selector(makeMyProgressBarMoving) withObject:nil waitUntilDone:NO];
then add below method
- (void)makeMyProgressBarMoving {
float actual = [threadProgressView progress];
if (actual < 1) {
threadProgressView.progress = actual + ((float)recievedData/(float)xpectedTotalSize);
[NSTimer scheduledTimerWithTimeInterval:0.05 target:self selector:#selector(makeMyProgressBarMoving) userInfo:nil repeats:NO];
}
else{
}
}
also give your review for answer. is it useful to you?
It is quite simple. You just need to set appropriate value of property progress of UIProgressView.
In delegate of NSURLConnection you should receive the amount of data you are waiting to download and update the progress during downloading. Progress is represented by a floating-point value between 0.0 and 1.0, inclusive, where 1.0 indicates the completion of the task.
You can display progress of progress bar with these line of code
-(void) connection:(NSURLConnection *) connection
didReceiveData:(NSData *) data {
if (file)
{
[file seekToEndOfFile];
progressView.progress = ((float)recievedData / (float) xpectedTotalSize);
}
[file writeData:data];
recievedData += data.length;
NSLog(#"Receiving Bytes: %d", recievedData);
}
One of the option is AFNetworking.
AFURLConnectionOperation also allows you to easily stream uploads and downloads, handle authentication challenges, monitor upload and download progress, and control the caching behavior or requests.
noted: self.progressionBalance.progress = 5.0/10.0;
you must set decimal.
Related
I'm very new to ios programming and I'm facing a problem regarding the update of labels during audio processing tasks.
I use a classic RecordingCallback -> ProcessAudio method. In ProcessAudio I would like to stop the recording if the level is too low. This was quite easy to do. But when it stops, I can't change the text label from "Recording" to "Stopped". It works great with the button (play/stop) but not when calling back. There is no error during compiling. Just nothing happens...
Here is the code :
-(void)processAudio:(AudioBufferList *)bufferList{
AudioBuffer sourceBuffer = bufferList->mBuffers[0];
// copy incoming audio data to temporary buffer
memcpy(tempBuffer.mData, bufferList->mBuffers[0].mData, bufferList->mBuffers[0].mDataByteSize);
int16_t* samples = (int16_t*)(tempBuffer.mData);
for ( int i = 0; i < tempBuffer.mDataByteSize / 2; ++i )
{
if (samples[i]< LevelTrigger)
{
Presence++;
if (Presence== 2 * SampleRate)
{
printf("Nothing");
//dispatch_async(dispatch_get_main_queue(), ^{
//[self buttonPressed:nil];
//});
//[self buttonPressed:nil];
[label performSelectorOnMainThread:#selector(setText:) withObject:#"TEST" waitUntilDone:YES];
Presence=0;
break;
}
}
As you can see, I have tried to use "dispatch_async" and "performSelectorOnMainThread" functions but it didn't help. The buttonPressed function is called correctly, but nothing happens.
Thanks for you help.
JC
This is what I did:
Cloned the repository provided by your link:
git clone https://github.com/jar-son/rioGraph.git
Can you read patch format? http://pastebin.com/Sc0eb3tR (link is valid for 30 days from now on) Summary of what I did:
Added a #class ViewController to Audio.h
Added a #property (nonatomic, assign) ViewController *viewController; to the Audio.h
Added [self.viewController performSelectorOnMainThread:#selector(setMyLabel) withObject:self waitUntilDone:NO]; to -(void)processAudio:(AudioBufferList *)bufferList in Audio.m
Added - (void)setMyLabel; to ViewController.h
Added
- (void)setMyLabel
{
self.label.text = #"blablabla!";
} to ViewController.m
...and it just worked.
PS: This explains how to apply a patch: https://ariejan.net/2009/10/26/how-to-create-and-apply-a-patch-with-git/
hi I want to ask a simple question how I can hide or disable progress bar when UIWebView load, I add ProgressBar as subview of webview . I did it by using this way in the method below, but it can't help me because every site take different time to load because of their content size so kindly tell me how I can hide or remove the ProgressBar when any site load in webview
- (void)makeMyProgressBarMoving {
float actual = [threadProgressView progress];
if (actual < 1) {
threadProgressView.progress = actual + 0.2;
[NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:#selector(makeMyProgressBarMoving) userInfo:nil repeats:NO];
}
else
{
threadProgressView.hidden = YES;
threadValueLabel.hidden = YES;
}
}
First add delegate to UIWebView
For adding progress bar :-
Web view delegate method :-
- (void)webViewDidStartLoad:(UIWebView *)webView
{
threadProgressView.hidden = NO;
}
For Removing progress bar :-
Web view delegate method :-
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
threadProgressView.hidden = YES;
}
Hope this helps you
Check your webview is loaded completly or not.
if(!yourWebView.loading)
{
[yourProgress removeFromSuperView];
}
loading
A Boolean value indicating whether the receiver is done loading
content. (read-only) #property(nonatomic, readonly, getter=isLoading) BOOL loading >
Discussion
If YES, the receiver is still loading content; otherwise, NO.
Availability
Available in iOS 2.0 and later.
or
You can implement the webViewDidFinishLoad delegate method of UIWebView.
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
[yourProgress removeFromSuperView];
}
webViewDidFinishLoad:
Sent after a web view finishes loading a frame.
- (void)webViewDidFinishLoad:(UIWebView *)webView
Parameters
webView
The web view has finished loading.
Availability
Available in iOS 2.0 and later.
Refer UIWebViewDelegate,UIWebView for more details
I have an app processing a couple of images using Quartz, and i wanted to have a UIProgressView that changes after each actions. (e.g. 0.0 0.2 0.4 0.6 0.8 1.0)
The problem is, it seems while my image is being processed the UI is completely locked, and the value only changes after all of the process is done (meaning it just gets to 1.0 without going through the sub-steps),
Did any of you ever encounter this ?
Pseudo:
for(uint i=0;i<5;i++){
// Execute some Quartz based action here, such as CGContextDrawTiledImage etc...
myProgress.progress = (i+1) * 0.2;
}
So actually instead of the progress bar changing after each action, it only changes once at the end to 1.0. Would appreciate your feedback or experience or this.
Thank you
Shai.
You'll need to update either your assets or your progress bar in a separate thread so that the two can update in parallel.
Have a look at [NSThread detachNewThreadSelector:selector toTarget:target withObject:object];
Make your progress a member variable
[NSThread detachNewThreadSelector:#selector(updateFilterProgress) toTarget:self withObject:nil];
prgLoader.progress = x;
- (void) updateFilterProgress{
NSAutoreleasePool *pool = [NSAutoreleasePool new];
while ([prgLoader.progress floatValue] < 1.0f) //Keep this thread alive till done loading. You might want to include a way to escape (a BOOL flag)
{
GTMLoggerInfo(#"Updating progress to %f", [progress floatValue]);
prgLoader.progress = [progress floatValue];
}
[pool release];
}
I am trying to create a audio meter level while I am recording the user voice using avaudiorecorder. Can someone help me in that regard?
Actually, the code is pretty straightforward since AVAudioPlayer and AVAudioRecorder have built in methods to get you on your way. My approach was this:
make a repeating call to -updateMeters and the averagePowerForChannel: & peakPowerForChannel: methods and call a delegate method to notify the controlling object
Example:
NSOperationQueue *queue=[[NSOperationQueue alloc] init];
NSInvocationOperation *operation=[[NSInvocationOperation alloc] initWithTarget:self selector:#selector(updateMeter) object:nil];
[queue addOperation: operation];
and
-(void)updateMeter
{
do {
//don't forget:
[recorder updateMeters];
self.averagePower = [recorder averagePowerForChannel:0];
self.peakPower = [recorder peakPowerForChannel:0];
// we don't want to surprise a ViewController with a method call
// not in the main thread
[self.delegate performSelectorOnMainThread: #selector(meterLevelsDidUpdate:) withObject:self waitUntilDone:NO];
[NSThread sleepForTimeInterval:.05]; // 20 FPS
}while(someCondition);
}
If your View Controller implements the meterLevelsDidUpdate: method, you can use this method to update your Level Meter.
create a UIView subclass with a subview that changes its height according to the average or peak value(s). Adjust to taste.
Easy, you can use NSTimer for that:
- (void)startAudioMetering {
self.meterTimer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:#selector(updateAudioMeter)userInfo:nil repeats:YES];
}
- (void)stopAudioMetering {
[self.meterTimer invalidate];
}
- (void)updateAudioMeter { //called by timer
// audioRecorder being your instance of AVAudioRecorder
[self.audioRecorder updateMeters];
self.dBLevel = [self.audioRecorder averagePowerForChannel:0];
}
WARNING: While creating your AVAudioRecorder instance, you have to call meteringEnabled AFTER you call prepareToRecord or record, otherwise it won't updateMeters:
[self.audioRecorder prepareToRecord];
self.audioRecorder.meteringEnabled = YES;
Swift code based on Tom's answer:
NSOperationQueue().addOperationWithBlock({[weak self] in
repeat {
self?.audioRecorder.updateMeters()
self?.averagePower = self?.audioRecorder.averagePowerForChannel(0)
self?.peakPower = self?.audioRecorder.peakPowerForChannel(0)
self?.performSelectorOnMainThread(#selector(DictaphoneViewController.updateMeter), withObject: self, waitUntilDone: false)
NSThread.sleepForTimeInterval(0.05)//20 FPS
}
while (someCondition)
})
Do the meter UI stuff inside func updateMeter(){//UI stuff here}
Its pretty simple,
The values you get in the buffer are positive and negative (this is how the waves work) so if you do the average of that values it will give you a near 0 value.
So what you have to do is just put all values positive (with the Math.abs() function) and then do the avarage, it will return you the sound level.
Hope this helps ;)
You can also use ReactiveCocoa and make use of interval:onScheduler::
Returns a signal that sends the current date/time every interval on scheduler.
Using a single audio channel:
#weakify(self)
RACDisposable *metersDisposable = [[RACSignal // make sure you dispose it eventually
interval:0.1
onScheduler:[RACScheduler scheduler]]
subscribeNext:^(NSDate *) {
#strongify(self)
[recorder updateMeters];
auto averagePower = [recorder averagePowerForChannel:0];
auto peakPower = [recorder peakPowerForChannel:0];
// Inform the delegate or make other use of averagePower and peakPower
}];
I found the answer in following link.
AVMeter for AVPlayer
Though it requires lot of customizations but I feel I will be able to do it.
EDIT: The problem i am having is that i am using the TapKu calendar, so i am relying on the provided delegates. Here is the problm:
- (NSArray*) calendarMonthView:(TKCalendarMonthView*)monthView marksFromDate:(NSDate*)startDate toDate:(NSDate*)lastDate{
//SOAP Request has NSURLConnection which runs asychonrous delegate methods.
//SOAP Request will return before the data array has been populated
[self SOAPRequest:startDate];
//i need to do something like this, however the SOAPRequest will return and get stuck into the loop instead of the asychronos delegates firing
//therefore i can never set bGettingData to NO.
int iWait;
while (bGettingData) {
iWait++;
}
return dataArray;
}
Hello,
In the app i am creating, i rely on SOAP requests to retrieve data, parse the XML and populate an array.
The problem i have, is that when i check the array it is empty, because the SOAP request has not completed. How do i stop my code from executing until the SOAP request is complete and resume the code? Can this be done through a callback or threading?
Thanks
Don't temporarily stop, sleep or wait, instead simply exit/quit/return from the current routine/function/method.
Break your current "stuff" into multiple fragments of code, each fragment in its own method.
Use subsequent method(s) to do whatever comes next, and have that method called by the completion routine of your async network/SOAP request.
Basically, your problem is that you are still thinking in terms of procedural coding. The proper paradigm is to use event driven coding: have the OS call your code, rather than having your code call the OS and waiting.
Yes,
start here: http://ondotnet.com/pub/a/dotnet/2005/08/01/async_webservices.html
You want indeed to wait for the answer to be complete - a callback is usually easiest. Exactly how depends on the programming library/language you are using (is above in javascript, objectiveC, did you hand code or start with an example).
Check out the answers to Iphone SOAP request step by step tutorial - such as http://macresearch.org/interacting-soap-based-web-services-cocoa-part-1 and http://brismith66.blogspot.com/2010/05/iphone-development-accesing-soap.html. Or follow https://developer.omniture.com/node/321 - which simply waits until the answer has fully arrived.
Unfortunately when using the TapKu calendar, you can not asynchronously load from a database via SOAP. You must synchronously load the calendar, because their is not way to refresh the calendar view once the data has finished loading. If you have 40+ records per month, this will create a huge 5-6 second delay.
This is indeed possible, as an example for the day calendar view, modify _refreshDataPageWithAtIndex to be like this:
- (void) _refreshDataWithPageAtIndex:(NSInteger)index{
UIScrollView *sv = self.pages[index];
TKTimelineView *timeline = [self _timelineAtIndex:index];
CGRect r = CGRectInset(self.horizontalScrollView.bounds, HORIZONTAL_PAD, 0);
r.origin.x = self.horizontalScrollView.frame.size.width * index + HORIZONTAL_PAD;
sv.frame = r;
timeline.startY = VERTICAL_INSET;
for (UIView* view in sv.subviews) {
if ([view isKindOfClass:[TKCalendarDayEventView class]]){
[self.eventGraveYard addObject:view];
[view removeFromSuperview];
}
}
if(self.nowLineView.superview == sv) [self.nowLineView removeFromSuperview];
if([timeline.date isTodayWithTimeZone:self.timeZone]){
NSDate *date = [NSDate date];
NSDateComponents *comp = [date dateComponentsWithTimeZone:self.timeZone];
NSInteger hourStart = comp.hour;
CGFloat hourStartPosition = hourStart * VERTICAL_DIFF + VERTICAL_INSET;
NSInteger minuteStart = round(comp.minute / 5.0) * 5;
CGFloat minuteStartPosition = roundf((CGFloat)minuteStart / 60.0f * VERTICAL_DIFF);
CGRect eventFrame = CGRectMake(self.nowLineView.frame.origin.x, hourStartPosition + minuteStartPosition - 5, NOB_SIZE + self.frame.size.width - LEFT_INSET, NOB_SIZE);
self.nowLineView.frame = eventFrame;
[sv addSubview:self.nowLineView];
}
if(!self.dataSource) return;
timeline.events = [NSMutableArray new];
[self.dataSource calendarDayTimelineView:self eventsForDate:timeline.date andEvents:timeline.events success:^{
[timeline.events sortUsingComparator:^NSComparisonResult(TKCalendarDayEventView *obj1, TKCalendarDayEventView *obj2){
return [obj1.startDate compare:obj2.startDate];
}];
[self _realignEventsAtIndex:index];
if(self.nowLineView.superview == sv)
[sv bringSubviewToFront:self.nowLineView];
}];
}
and then change your eventsForDate function to look like this:
- (void) calendarDayTimelineView:(TKCalendarDayView*)calendarDayTimeline eventsForDate:(NSDate *)eventDate andEvents:(NSMutableArray *)events success:(void (^)())success {
[Model doSomethingAsync andSuccess:^(NSArray *classes) {
// .. Add stuff to events..
success();
}];
}
I'm assuming the pattern for the other controls is very similar. The premise is you're waiting to continue the formatting/layout flow til you get your data.