Create UITextViews in a loop - Objective C - iphone

I'm working on an app in which I need to scan through a string to populate some UITextViews. Basically the data is like this:
Time Period: 10am-12pm
Temperature: 45F
Wind: 123 degrees # 5mph
Time Period: 1am-3pm
Temperature: 53F
Wind: 133 degrees # 2mph
Time Period: 4am-5pm
Temperature: 50F
Wind: 110 degrees # 7mph
The problem is that there is not a set number of time periods that are available at any given time. So I just have to loop through until I reach the end. Is there a way to create a textview inside a loop?
UITextView *textField1 = [[UITextView alloc] initWithFrame:CGRectMake(0, 0, 0, 0)];
UITextView *textField2 = [[UITextView alloc] initWithFrame:CGRectMake(0, 0, 0, 0)];
UITextView *textField3 = [[UITextView alloc] initWithFrame:CGRectMake(0, 0, 0, 0)];
.....
So could I create the textfields at runtime until it reaches the end of the string or would I have to create the max number that it could possibly need then just use them if needed?
Thanks,
Andrew Boos

Sure you can.
First divide your data string using either [NSString componentsSeparatedByString] or [NSString componentsSeparatedByCharactersInSet] to get an NSArray of substrings.
//assuming your data is separated by newlines
NSArray * substrings = [data_input componentsSeparatedByString:#"\n"];
//loop over the substrings creating textfields
for (int i = 0; i < [substrings count]; i++)
{
CGRect frame = CGrectMake(0, i * 40, 100, 30);
UITextField * tf = [[UITextField alloc] initWithFrame: frame];
tf.text = [substrings objectAtIndex:i];
//add as subview
[view addSubview: tf];
//if you are not using ARC release the textfield
}
//disclaimer: written this on the go, may contain some spelling mistakes etc, but should be enough to get you going.

Related

To make 16 uilabels and align them in circular path.

NSMutableArray *views = [[NSMutableArray alloc]initWithCapacity:0];
for (NSInteger i = 0; i<16; i++)
{
UIView *circle = [[UIView alloc]init];
circle.backgroundColor = [UIColor clearColor];
UIImageView *circleImage = [[UIImageView alloc]initWithFrame:CGRectMake(0, 0, 80, 80)];
circleImage.image = [UIImage imageNamed:#"circle"];
[circle addSubview:circleImage];
UILabel *labelInsideCircle = [[UILabel alloc]initWithFrame:CGRectMake(10, 10, 40, 40)];
labelInsideCircle.backgroundColor = [UIColor clearColor];
labelInsideCircle.textColor = [UIColor greenColor];
labelInsideCircle.font = [UIFont fontWithName:#"Helvetica" size:30.0];
labelInsideCircle.center = circleImage.center;
NSInteger int_ = [self getRandomNumber:0 to:(arrOfOptions.count-1)];
labelInsideCircle.text = [NSString stringWithFormat:#"%#",[arrOfOptions objectAtIndex:int_]];
labelInsideCircle.textAlignment = NSTextAlignmentCenter;
[arrOfOptions removeObjectAtIndex:int_];
[circle addSubview:labelInsideCircle];
[labelInsideCircle release];
[views addObject:circle];
[circle release];
[circleImage release];
}
/* Rotating circles with angles */
float curAngle = 0;
float incAngle = ( 360.0/(views.count) )*3.14/180.0;
CGPoint circleCenter = CGPointMake(380, 580); /* given center */
float circleRadius = 250; /* given radius */
for (UIView *view in views)
{
CGPoint viewCenter;
viewCenter.x = circleCenter.x + cos(curAngle)*circleRadius;
viewCenter.y = circleCenter.y + sin(curAngle)*circleRadius;
view.transform = CGAffineTransformRotate(view.transform, curAngle);
view.center = viewCenter;
[self.view addSubview:view];
curAngle += incAngle;
}
The problem is here the text of UILabel is also getting transformed, which is obvious. What I want is 16 circular views with labels on them without the label's text transformed. Can anyone please help me out with this ?
In this case, you just need to change their location coordinates, not rotate them.
NSMutableArray *views = [[NSMutableArray alloc] initWithCapacity:0];
for (NSInteger i = 0; i<16; i++)
{
UIView *circle = [[UIView alloc]init];
circle.backgroundColor = [UIColor clearColor];
UIImageView *circleImage = [[UIImageView alloc]initWithFrame:CGRectMake(0, 0, 80, 80)];
circleImage.image = [UIImage imageNamed:#"circle"];
[circle addSubview:circleImage];
UILabel *labelInsideCircle = [[UILabel alloc]initWithFrame:CGRectMake(10, 10, 40, 40)];
labelInsideCircle.backgroundColor = [UIColor clearColor];
labelInsideCircle.textColor = [UIColor greenColor];
labelInsideCircle.font = [UIFont fontWithName:#"Helvetica" size:30.0];
labelInsideCircle.center = circleImage.center;
NSInteger int_ = arc4random()%[arrOfOptions count];
labelInsideCircle.text = [NSString stringWithFormat:#"%#",[arrOfOptions objectAtIndex:int_]];
labelInsideCircle.textAlignment = NSTextAlignmentCenter;
[arrOfOptions removeObjectAtIndex:int_];
[circle addSubview:labelInsideCircle];
[labelInsideCircle release];
[views addObject:circle];
[self.view addSubview:circle];
[circle release];
[circleImage release];
}
/* Rotating circles with angles */
float curAngle = 0;
float incAngle = ( 360.0/(views.count) )*3.14/180.0;
CGPoint circleCenter = CGPointMake(380, 580); /* given center */
float circleRadius = 250; /* given radius */
for (UIView *view in views)
{
CGPoint viewCenter;
viewCenter.x = circleCenter.x + cos(curAngle)*circleRadius;
viewCenter.y = circleCenter.y + sin(curAngle)*circleRadius;
//view.transform = CGAffineTransformRotate(view.transform, curAngle);
view.center = viewCenter;
[self.view addSubview:view];
curAngle += incAngle;
}
Some coding suggestions:
You can use arc4random() function if you don't have anything special in your own random number generator.
You can turn on ARC in xCode!
In a loop set their centers accordingly, don't rotate them nor their super view.
The following is not directly related to your question but may be helpful:
Aparaently you have got all the math available. I suggest to measure how much cpu time gets lost on the cos and sin etc. calls. If you find that significant then think about your algorithm. You will (most probably) find out that you call cos and sin hundrets or thousands of times for a limited number of angles.
You may then try it and find out that possible pre-calculations or just "caching and reusing" earlier results may save significant processing time.
Plus pre-calculating or caching of sinus would do. You can derrive cosinus values from sinus (and vice versa) by adding (or substracting respectively) an offset of pi/2 (or 90 degrees respectively) to the argument.
For similar tasks I was working with degrees (not radiants) and found out that I could not predict the angles but that a significant of full degrees (1°, 2°, 3°, ... and nothing in between) was exact enough for the job. Then I maintained an array of 360 sinus values and used that instead of calling the real function again and again. (Plus I only had to calculate 90 of them and mirrored the results of the other 270 degrees according to the nature of the sinus function) 180 floats is not too much of memory compared to the speed that I gained. Something like that can be suitable for you too. In your case your potenital agruments to sin and cos are limited to full-number multipilers of incAngle.
Which means there are only [views count] number of potential values each.

How to change the XAxis used on my chart ShinobiCharts

I'd like to have the xAxis of my chart to look like this :
Jan, Feb, Mar, Apr, ..... Nov, Dec
Right now it's following it's default, numbering xAxis according to the number of Data Points.
How can i achieve this change to this axis ?
I've tried using Category Axis and setting an NSMutableArray containing these strings ("Jan", "Feb"...) as categories and with a numberRange going from 1 to 12 but it didn't work.
chart = [[ShinobiChart alloc] initWithFrame:chartEmbaixo.frame withPrimaryXAxisType:SChartAxisTypeCategory withPrimaryYAxisType:SChartAxisTypeNumber];
NSMutableArray * monthNames = [[NSMutableArray alloc] initWithObjects:#"Jan", #"Fev", #"Mar", #"Abr", #"Mai", #"Jun", #"Jul", #"Ago", #"Set", #"Out", #"Nov", #"Dez", nil];
SChartNumberRange * numberRange = [[SChartNumberRange alloc] initWithMinimum:[NSNumber numberWithInt:1]andMaximum:[NSNumber numberWithInt:12]];
SChartCategoryAxis *xAxis = [[SChartCategoryAxis alloc] initWithRange:numberRange];
xAxis.categories = monthNames;
chart.xAxis = xAxis;
first i use as my x axis
Edit how i make my x axis:
SChartNumberRange *r1 = [[SChartNumberRange alloc] initWithMinimum:[NSNumber numberWithInt:0] andMaximum:[NSNumber numberWithInt:2]];
SChartCategoryAxis *xAxis = [[SChartCategoryAxis alloc] initWithRange:r1];
xAxis.title = #"";
//xAxis.enableGesturePanning = YES;
xAxis.enableGesturePanning = YES;
xAxis.style.gridStripeStyle.showGridStripes = NO;
xAxis.style.majorGridLineStyle.showMajorGridLines = NO;
when you make you data points it should use the xValue as the x axis point.
like this:
dp.yValue = 1000;
dp.xValue = #"Jan";
the xValue should be set as the x point for that particular data point. This should work, but if it doesn't or you want to do something more complex you can extend this method from SChartDelegate protocol:
-(void)sChart:(ShinobiChart *)chart alterTickMark:(SChartTickMark *)tickMark beforeAddingToAxis:(SChartAxis *)axis
in this method you have the tickMark.tickLabelis the axis label for that given point where you can do your editing. Don't forget to verify what axis your on.
Hope this helps. If not tomorrow i can post you some code from my project (currently i don't have access to it from where i am)
Edit: currently i have this code:
- (void)sChart:(ShinobiChart *)chart alterTickMark:(SChartTickMark *)tickMark beforeAddingToAxis:(SChartAxis *)axis {
if (chart.yAxis == axis ) return;
for (UIView *i in tickMark.tickMarkView.subviews)
[i removeFromSuperview];
tickMark.tickMarkView.frame = CGRectMake(0, 0, 170, 75);
//center the marker at the right place because the size was changed
tickMark.tickMarkX = tickMark.tickMarkX - (tickMark.tickMarkView.frame.size.width/2) ;
tickMark.tickMarkY = 10;
//img
UIImageView *img = [[UIImageView alloc] initWithImage:[UIImage imageNamed: #"graph_bar_tag_2#2x.png"]];
img.frame = CGRectMake( 0, 0, tickMark.tickMarkView.frame.size.width, tickMark.tickMarkView.frame.size.height);
[tickMark.tickMarkView addSubview:img];
//label with the markView's size with 7px padding on the left and on the right
UILabel *label = [[UILabel alloc] initWithFrame: CGRectMake( 7, 5, tickMark.tickMarkView.frame.size.width-14, 15)];
label.backgroundColor = [UIColor clearColor];
//tikMark.tickLabel has an pair of indexes so that i can easily find the data for this particular data point and series.
label.text = [_dataSource getNameFor: tickMark.tickLabel.text];
label.textAlignment = UITextAlignmentCenter;
//color_other_light is a UIColor var
[label setTextColor: color_other_light];
[tickMark.tickMarkView addSubview:label];
...
}

UILabel default kerning different from CATextLayer

I have a UILabel with the string 'LA'. I also have a CATextLayer with the same characters in an NSAttributedString assigned to its string property. The kerning in the UILabel is noticeably different from the CATextLayer. Here's the code.
- (void)viewDidLoad
{
[super viewDidLoad];
//
// UILabel
//
UILabel *label1 = [[UILabel alloc] initWithFrame:CGRectMake(20, 50, 280, 100)];
label1.text = #"LA";
label1.backgroundColor = [UIColor clearColor];
label1.font = [UIFont fontWithName:#"Futura" size:90.0];
[self.view addSubview:label1];
//
// CATextLayer
//
UILabel *label2 = [[UILabel alloc] initWithFrame:CGRectMake(20, 130, 280, 100)];
label2.backgroundColor = [UIColor clearColor];
CATextLayer *textLayer = [[CATextLayer alloc] init];
textLayer.frame = label2.layer.bounds;
textLayer.contentsScale = [[UIScreen mainScreen] scale];
[label2.layer addSublayer:textLayer];
NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithString:#"LA"];
CTFontRef aFont = CTFontCreateWithName((__bridge CFStringRef)#"Futura", 90.0, NULL);
[string addAttribute:(NSString*)kCTFontAttributeName value:(__bridge id)aFont range:NSMakeRange(0, [string length])];
textLayer.string = string;
[self.view addSubview:label2];
}
Here's an image of the results.
Why is the kerning different between these two methods and what am I doing wrong in the CATextLayer example?
UIKit generally uses WebKit for its text rendering (as visible in this crash log), most likely for performance reasons. If you really need super-precision then there are some custom UILabel reimplementations using CoreText as its back-end.
EDIT:
As of iOS7 this is no longer true since UILabel uses TextKit for its rendering which is based on CoreText as well.
you should add attribute to your NSMutableAttributedString.
For the kerning:
CGFloat characterspacing = 10.0f;
CFNumberRef num = CFNumberCreate(kCFAllocatorDefault,kCFNumberSInt8Type,&characterspacing);
[string addAttribute:(id)kCTKernAttributeName value:(id)num range:NSMakeRange(0 , [string length])];
CFRelease(num);
If you also need the line spacing, or set LineBreadMode:
CTLineBreakMode linebreak = kCTLineBreakByCharWrapping;
CTParagraphStyleSetting linebreakStyle;
linebreakStyle.spec = kCTParagraphStyleSpecifierLineBreakMode;
linebreakStyle.valueSize = sizeof(linebreak);
linebreakStyle.value = &linebreak;
CTParagraphStyleSetting lineSpaceStyle;
CGFloat linespacing = self.linesSpacing;
lineSpaceStyle.spec = kCTParagraphStyleSpecifierLineSpacingAdjustment;
lineSpaceStyle.valueSize = sizeof(linespacing);
lineSpaceStyle.value =&linespacing;
CTParagraphStyleSetting settings[ ] ={linebreakStyle,lineSpaceStyle};
CTParagraphStyleRef style = CTParagraphStyleCreate(settings ,2);
[string addAttribute:(id)kCTParagraphStyleAttributeName value:(id)style range:NSMakeRange(0 , [string length])];
CFRelease(style);
At the end, may you need calculate the number of line(linenum) about your kerning,line spacing and LineBreakMode:
CTFramesetterRef myframesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)string);
CGMutablePathRef leftColumnPath = CGPathCreateMutable();
CGPathAddRect(leftColumnPath, NULL ,CGRectMake(0 , 0 , Lable.frame.size.width, MAXFLOAT));
CTFrameRef leftFrame = CTFramesetterCreateFrame(myframesetter,CFRangeMake(0, 0), leftColumnPath , NULL);
CFArrayRef lines = CTFrameGetLines(leftFrame);
linenum = (int)CFArrayGetCount(lines);
CFRelease(myframesetter);
CFRelease(leftFrame);
CGPathRelease(leftColumnPath);
Well Core Text is really different when compared to drawing strings using UIKit, probably because it comes from Core Foundation and not AppKit or UIKit. I do understand your requirements to use a label for doing the metrics hard job on a string. The only solution for me is to match the kerning of UILabel in the attributed string, unfortunately I don't know the exact value but you can use this property to change that value kCTKernAttributeName. You should pay attention also for the interline that could be not the same.
Forcing that value to the matching kerning you could have the correct behavior. If you want the opposite (match CT kerning) you should do some math an later apply to the label a UIEdgeInset to math the correct label.
Hope this helps.

how to get current uilabel text from uiscroll view runtime

I have task to develop the application with words counting and display in runtime generated uiscrollview with uilabel as subview.
The process is like when user will load page at that time the 1000 of word will fill in the rutime generated uilabel with scrollview.And all thing is set from runtime. but it the application we have one button to add the current uilabel text on button click.
and the words are coming random from database. when i click on the button it gives me the different word. from scroll view displayed word.
Following is my code to fill the data in uilabel with scrollview :
wordName = [NSString stringWithUTF8String:(char *)sqlite3_column_text(searchStatement, 2)];
[wordNameArray addObject:wordName];
int i = 0;
for (i = 1; i <= kNumImages; i++)
{
randomN = [wordNameArray objectAtIndex:arc4random() % [wordNameArray count]];
UIImage *image = [UIImage imageNamed:#"word_bg.png"];
word = [[UILabel alloc]initWithFrame:CGRectMake(15,12,250,120)];
[word setFont:[UIFont fontWithName:#"Helvetica-Bold" size:36.0f]];
word.textColor = [UIColor whiteColor];
word.lineBreakMode = UILineBreakModeWordWrap;
word.numberOfLines = 2;
word.textAlignment = UITextAlignmentCenter;
word.backgroundColor = [UIColor clearColor];
[word setText:randomN];
lblWord.text = word.text;
word.tag = i;
NSLog(#"tag no:%d",word.tag);
imageView = [[UIImageView alloc] initWithImage:image];
[imageView addSubview:word];
CGRect rect = imageView.frame;
rect.size.height = kScrollObjHeight;
rect.size.width = kScrollObjWidth;
imageView.frame = rect;
imageView.tag = i;
//NSLog(#"%#",word.text);
//NSLog(#"%#",lblWord.text);
// [scrollView1 addSubview:l];
[scrollView1 addSubview:imageView];
touchToSee.hidden = TRUE;
[imageView release];
}
[self layoutScrollImages];
Following is my scroll event to get the word from label but it is giving me last word from array:
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
touchToSee.hidden = TRUE;
NSLog(#"word-%#",word.text);
}
And other when i click on button i am not getting the perfect work so for that following is my code:
NSString *getword = [NSString stringWithFormat:#"%#",word.text];
NSLog(#"lblCountdownBackward:%#",word.text);
So please help me and provide me some sample code for it if it possible.
thanks in advance.
You appear to be creating lots of word objects, and adding them to the screen.
In your for loop, you perform your code upto kNumImages. And in each run, the word Object is replace with a new one.
So when you get the word later on, it will be the very last word object you created (hence the last word).
To get arround this you could use an NSMutableArray and keep adding a new UILabel object to that (instead of creating the word object). And then later on do something like
[array objectAtIndex:0].text;

want to create a variable, increment it and get new value

i'm creating a bunch of textfields. i am stacking them vertical, so i use:
CGRectMake(193, ((i * 45) + 45), 240, 30)
then after each textbox is created, i increment i.
int i = 0
CGRect myRect = CGRectMake(193, ((i * 45) + 45), 240, 30)
UITextField *myTextField01 = [[UITextField alloc] initWithFrame:myRect];
//format and add to view text field
i++;
//create next text field
myRect = CGRectMake(193, ((i * 45) + 45), 240, 30) //can i get rid of this line?
UITextField *myTextField01 = [[UITextField alloc] initWithFrame:myRect];
//format and add to view text field
i++;
i need to reassign the myRect to get i updated to the value of 1.
is there a better way to do this so i don't need to reassign myRect to get the update value of i?
CGRect is a struct, so you can increment the internal fields directly:
CGRect myRect = CGRectMake(193, 45, 240, 30);
UITextField *myTextField01 = [[UITextField alloc] initWithFrame:myRect];
myRect.origin.y += 45;
UITextField *myTextField02 = [[UITextField alloc] initWithFrame:myRect];
myRect.origin.y += 45;
UITextField *myTextField03 = [[UITextField alloc] initWithFrame:myRect];
I would also use a loop and some kind of container (probably NSArray), but that's a side topic ;)
Use myRect = CGRectOffset(myRect, 0, 45); each time. "i" is not used here because you offset the already offset rectangle.