Showing percent in label - ios

I use code for showing the progress of downloading a file from 1% to 100%.
if( downloadTask == _downloadTask26){
_progress26 = [[NSNumber numberWithInteger:totalBytesWritten] floatValue];
_total26 = [[NSNumber numberWithInteger:totalBytesExpectedToWrite] floatValue];
}
if( downloadTask == _downloadTask27){
_progress27 = [[NSNumber numberWithInteger:totalBytesWritten] floatValue];
_total27 = [[NSNumber numberWithInteger:totalBytesExpectedToWrite] floatValue];
}
float progress = _progress26 + _progress27;
float total = _total26 + _total27;
NSString *percentage = [NSString stringWithFormat:#"%.f%%", ((progress / total) * 100)];
(NSLog (percentage, #"%.f%%"));
if (!_label3) {
_label3 = [[UILabel alloc] initWithFrame:CGRectMake(200.43, 158.84, 42, 19)];
_label3.numberOfLines = 1;
_label3.baselineAdjustment = UIBaselineAdjustmentAlignBaselines;
_label3.adjustsFontSizeToFitWidth = YES;
_label3.minimumScaleFactor = 10.0f/12.0f;
_label3.clipsToBounds = YES;
_label3.backgroundColor = [UIColor clearColor];
_label3.textColor = [UIColor whiteColor];
_label3.textAlignment = NSTextAlignmentCenter;
[_scroller addSubview:_label3];
}
}
_label3.text = percentage;
if ([_label3.text isEqual: #"100%"]) {
}
But when file downloading, percent is not displayed in ascending order. Percent is displayed in a different order like in the video below. How do I fix it?
video
https://drive.google.com/file/d/0B0EJbcHq3ZALUVRzanJ6SndscWc/view

May be it's due to downloading task condition which check it is downloadingTask26 or 27 put logs inside this two conditions and check downloaded and total size . Total task may be changes.

Have you simplified the code in your question, where the code generating the video has more that just downloads 26 and 27 included? With only two downloads, I'd expect it to drop down only once, but if you have many downloads, I'd expect it to drop down each time a new download start.
Bottom line, if you really have many downloads, the result makes perfect sense because it's quite likely that you don't have values for total bytes for many of the downloads until they actually start and when a later download starts, suddenly the sum of total bytes downloaded will increase (and thus the percentage will drop).
One solution is to use NSProgress, create a child NSProgress for each download up front and then update the percentage for parent progress. That way, downloads that haven't started yet will be reflected in the total percentage.

Related

Card dealing, playing

Problem
I've never made a card game before, and I'm having quite some difficulty at the moment.
However, I've managed to create the deck and such.
-(NSMutableArray *)arrayWithDeck:(id)sender
{
[sender removeAllObjects];
NSArray *faces = [[NSArray alloc] initWithObjects:#"A",#"2",#"3",#"4",#"5",#"6",#"7",#"8",#"9",#"10",#"J",#"Q",#"K", nil];
NSArray *suits = [[NSArray alloc] initWithObjects:#"h",#"d",#"c",#"s", nil];
for (int i = 0; i < 52; i++) {
NSString *cardToAdd = [NSString stringWithFormat:#"%#%#", faces[i % 13], suits[i / 13]];
[sender addObject:cardToAdd];
}
return sender;
}
Then deal the cards to the players ( just to one player at the moment )
-(void) dealPlayersWithPlayers: (int) players withDealtCards: (int) dealt {
if (players == 2){
__block float add = 0;
for (int i = 0; i < dealt; i++) {
add = add + ((self.frame.size.width / 100) * 10);
NSString *imageName = [NSString stringWithFormat:#"%#", deck[i]];
NSString *bundle = [[NSBundle mainBundle] pathForResource:imageName ofType:#"png"];
UIImage *image = [[UIImage alloc] initWithContentsOfFile:bundle];
SKTexture *texture = [SKTexture textureWithImage:image];
cardDisplay = [SKSpriteNode spriteNodeWithTexture:texture];
cardDisplay.size = CGSizeMake(104, 144);
cardDisplay.anchorPoint = CGPointMake(0.5, 0.5);
cardDisplay.position = CGPointMake(-self.frame.size.width/2.5 + add, -218);
cardDisplay.zPosition = 1;
cardDisplay.userInteractionEnabled = NO;
cardDisplay.name = [NSString stringWithFormat:#"card"];
[self addChild:cardDisplay];
}
}
}
Then when touchesBegan, on a card it animates to the centre of the screen, i.e a pre-phase for a "lay cards" button. However, I'm really struggling to figure a way to keep track of the card pressed. I.e, is the card Js, Ah, 8c or whatever it could be so it can obviously be used, However the SKSpriteNode.name is already taken for the detection on touchesBegan.
Another issue I'm having is when the cards are layed. They are messing up the z-index. However, an easy fix for this could be to just keep incrementing the z-index but is it the best way of doing it? My example here shows what I'm talking about.
I would say the fact that you have them rendering and moving up you are off to a great start.
My recommendation though would be to look at a MVC (Model View Controller) approach to the problem. Keep the info for cards played and what the player has for cards in a separate object from your view. That way when you touch your card you can have your controller work with your model to identify it and decide what happens next. It will be very hard to manage the game if you are relaying only on the SKSpriteNode and looking at its name with no pointer to it to compare to anything.
As far as sorting your z index your model would know which card was added first in your hand and your controller can then inform the view the appropriate position and z index.
At the very least I would consider subclassing SKSpriteNode and make a CardSpriteNode at least then you don't have to look at the sprite name for touch and could just check to see if it is a CardSpriteNode.
if ([node isKindOfClass:[CardSpriteNode class]])
//handle touch logic if a card
MVC is a simple concept and I would look at some write ups on that. Everyone has a slightly different approach to what can see what, but all agree on keeping the information separate.
Hope that helps.

Xcode - How to draw a View using thread?

I have graphic view which is drawn using -(void)drawRect:(CGRect)rect function, it is a little complex graphic which includes date as X axis and time as Y axis. So every time I draw it, it always takes 2,3 seconds to display the final graphic.The red point maybe red, green or yellow, its colour depends on the data read from data base.
My question is that how to draw this view using multithread or in a fast way.
For example, draw the X axis and Y axis in one thread, and draw each day's graphic in separated thread(using 30 threads) or draw each hour for 30 days' graphics in separated thread(using 5 threads)?
I have tried using the following function:
for(int i = 0; i < 31; i++){
NSString *threadStr = [NSString stringWithFormat:#"%i", i];
dispatch_queue_t queue1 = dispatch_queue_create([threadStr UTF8String], NULL);
dispatch_async(queue1, ^{
//Draw my graphics for one day
.....
});
}
But sometimes these threads read the database at the same time and some of them cannot get the data properly, this cause the final graphic is not complete.
Could anyone give me some suggestion?
Thank you very much.
I am trying to find out the reason why it delays 2, 3 seconds if I draw it using main thread.
Is it because of reading SQLite3 Data base ?
The procedure of drawing this graphic is :
- (void)drawRect:(CGRect)rect
{
1.draw X axis with date label;
2.draw Y axis with time label;
NSArray *timeArray = #[#"08:00", 11:00", 14:00", 17:00", 20:00"];
for(NSString *timeStr in timeArray){
for(int i = 0; i < 31; i++){
NSString *DateTimeStr = dateStr + timeStr;
int a = [DataManager getResult: DateTimeStr];
UIColor *RoundColor;
if(a == 1){
RoundColor = [UIColor colorWithRed:229/255.0 green:0/255.0 blue:145/255.0 alpha:1.0];
}
else if(a == 2){
RoundColor = [UIColor colorWithRed:0/255.0 green:164/255.0 blue:229/255.0 alpha:1.0];
}
else if(a == 3){
RoundColor = [UIColor colorWithRed:229/255.0 green:221/255.0 blue:0/255.0 alpha:colorAlpha];;
}
const CGFloat *components = CGColorGetComponents(RoundColor.CGColor);
CGContextSetRGBFillColor(context, components[0], components[1], components[2], components[3]);
CGContextFillEllipseInRect(context, CGRectMake(roundXnow,roundYnow,roundR,roundR));
}
}
}
I am thinking if the delay occurs when I am reading the SQLite3 data base, because I know there are a lot of record in my data base.
Drawing here is a very light operation, most likely the timing issues you're facing are related to the calculations (or fetching of data) or whatever else you're doing before actually drawing the dot. So use instruments, time profiler in particular. It will show you what operations take longer (look at the pikes in the graphic that will be displayed while your app is running).
This will give you the clue.
Besides as it's correctly mentioned here, all the UI should be performed in the main thread.
But those pre-calculations and fetches that you probably have with your data - that's where you should look for multithreaded solutions if it's at all possible.

Changing the properties of a UIImageView

I have picked up programming as a hobby, so please bear with any 'old school' or completely wrong practices!
I am trying to move three images across the view using a timer. They then need to be dodged by another UIImageView. The obstacles are in an array:
objArray = [NSMutableArray arrayWithObjects:[UIImage imageNamed:#"img-1.png"],
[UIImage imageNamed:#"img-2.png"],
[UIImage imageNamed:#"img-3.png"], nil];
I then have a for loop to create these views, and it's here that I think the problems start.
count = [objArray count];
for(int i=0; i < count; i++)
{
NSLog (#"Element %i = %#", i, [objArray objectAtIndex: i]);
randX = arc4random_uniform(450);
randX = randx + 50;
randY = arc4random_uniform(236);
randY = randy + 45;
imgObj = [[UIImageView alloc] initWithImage:[UIImage imageNamed:[objArray objectAtIndex:i]]];
imgObj.center = CGPointMake(randX, randY);
[self.view addSubview:imgObj];
[self startAnimation];
All three images display ok. The problem is getting these to move.
I understand the need for the timer, which is initiated in the startAnimation method. I have tried setting a tag in the for loop. However this always results in the last object "img-3.png", which then moves ok.
My question is: how do I differentiate the three views I create in the loop so I can call them elsewhere?
Assigning tags to you UIImageViews should work:
imgObj.tag = i+1; //skipping 0 here since other subviews may have this tag
Then you should be able to access the views via
[self.view viewWithTag:tag];
Or you could create another NSMutableArray and store the UIImageViews in it.

Force JBChartView to use a 0-100 Y-axis

I'm using the excellent JBChartView in my project and It's working great. Here's my current code:
self.lineChart = [[JBLineChartView alloc] initWithFrame:CGRectMake(320, 400, 680, 300)];
self.lineChart.dataSource = self;
self.lineChart.delegate = self;
self.lineChart.backgroundColor = [UIColor clearColor];
JBLineChartFooterView *footerView = [[JBLineChartFooterView alloc] initWithFrame:CGRectMake(10, ceil(self.view.bounds.size.height * 0.5) - ceil(10 * 0.5), self.view.bounds.size.width - (10 * 2), 10)];
footerView.backgroundColor = [UIColor clearColor];
footerView.leftLabel.text = #"yesterday";
footerView.leftLabel.textColor = [UIColor whiteColor];
footerView.rightLabel.text = #"now";
footerView.rightLabel.textColor = [UIColor whiteColor];
footerView.sectionCount = 10;
self.lineChart.footerView = footerView;
[self.view addSubview:self.lineChart];
[self.lineChart setState:JBChartViewStateCollapsed animated:YES];
Nothing fancy, really.
JBChartView automatically chooses the Y-axis scale based on what data is passed to it in lineChartView: heightForIndex:. This is nice in most situations. My data, however, is on CPU usage, and thus I'd like to force the Y-axis to always be 0 at the bottom and 100 at the top.
Actually, I'd like it to be 0 on the bottom and MAX(100, highestPoint) at the top (because CPU usage can sometimes be over 100%) - is this possible?
As of v2.2.1, JBChartView allows you to supply a minimumValue and/or maximumValue for the y-axis.
Notes:
If no value(s) are supplied, the min and max values of the chart's data source are used.
If value(s) are supplied, they must be >= 0, otherwise an assertion will be thrown.
The min/max values are clamped to the ceiling and floor of the actual min/max values of the chart's data source.
For example, if a maximumValue of 20 is supplied & the chart's actual max is 100, then 100 will be used.
Lastly, for min/max modifications to take effect, reloadData must be called.
Hope this helps!
It turned out that all I needed to do was modify line 240 of JBLineChartView.m:
self.cachedMaxHeight = maxHeight;
to say:
self.cachedMaxHeight = MAX(100,maxHeight);

AVSpeechSynthesizer postUtteranceDelay timing problems

The code below takes a list of directions and reads them out using AVSpeechSynthesizer. Once complete, the user will be able to select a variable amount of time and the app will read out the instructions to fit the time span.
The problem is that when I press the play button, the delay between directions is significantly longer than it should be. Instead of the two minutes I've hardcoded it with, it takes over three. I've logged the value of all my postUtteranceDelays and they add up properly. It's also not due to processing time because when set postUtteranceDelay to 0 there is no pause between directions. I'm not sure what is going on.
- (IBAction)play:(UIButton *)sender {
[sender setTitle:#"Showering" forState:UIControlStateNormal];
Shower *shower = [[SpecificShower alloc] init];
NSUInteger totalRatio = [shower calculateTotalRatio:shower];
NSNumber *offset = #18.0; // estimated time to speak instructions combined
NSNumber *seconds = #120.0; // hard coded but just for testing
int totalSeconds = seconds.intValue - offset.intValue;
self.synthesizer = [[AVSpeechSynthesizer alloc] init];
for (NSDictionary* direction in shower.directions) {
AVSpeechUtterance *aDirection = [[AVSpeechUtterance alloc] initWithString:direction[#"text"]];
NSNumber *directionLength = direction[#"length"];
aDirection.rate = .3;
aDirection.preUtteranceDelay = 0;
// totalRatio is calculated by adding all the lengths together
// then the individual direction length is divided by totalRatio
// and that fraction is multiplied by total number of seconds
// to come up with the postUtteranceDelay for each direction
aDirection.postUtteranceDelay = totalSeconds * [directionLength floatValue]/totalRatio;
NSLog(#"%f", aDirection.postUtteranceDelay);
[self.synthesizer speakUtterance:aDirection];
}
}
You're not alone. This seems to be a bug as noted with a workaround over here.
There are also radars filed here and here.
Let's hope this gets fixed soon.

Resources