I need to create a a bunch a views on the fly. I was wondering what would be the best way to go about doing this as ill need to define coordinates, tag, colours of each view.
Would i need to create a multidimensional array and if so how can i do this?
CGRect viewRect1 = { 80.0, 200.0, 160.0, 100.0 };
UIview *myview1 = [[UIview alloc] initWithFrame:viewRect1];
[myview1 setBackgroundColor:[UIColor darkGrayColor]];
You need to define a structure to hold the data which is needed to create your UIView.
#interface MyViewDataHolder :NSObject
{
CGRect mViewRect;
UIColor* mDarkGrayColor;
NSInterger mTag;
}
#end
Then create an object of above class AND assign the values in member then add that in your NSArray...
EDITED:
In MyViewDataHolder.h class
#interface MyViewDataHolder :NSObject
{
CGRect mViewRect;
UIColor* mDarkGrayColor;
NSInteger mTag;
}
#property (nonatomic,assign) CGRect mViewRect;
#property (nonatomic,retain) UIColor* mDarkGrayColor;
#property (nonatomic,assign) NSInteger mTag;
#end
In MyViewDataHolder.mm class
#import "MyViewDataHolder.h"
#implementation MyViewDataHolder
#synthesize mViewRect,mDarkGrayColor,mTag;
-(void) dealloc
{
[mDarkGrayColor release]
mDarkGrayColor = nil;
}
Now How to use it ....
Create objects of MyViewDataHolder like below ...
MyViewDataHolder* myObj1 = [[MyViewDataHolder alloc] init];
myObj1.mViewRect = CGRectMake(x,y,width,height);
myObj1.tag = 1;
myObj1.mDarkGrayColor = [UIColor redColor];
Create as much as you need
Then Create an NSMutableArray and add each objects of MyViewDataHolder into NSMutableArray.
NSMutableArray* myArray = [[NSMutableArray alloc] init];
[myArray addObject:myObj1];
[myArray addObject:myObj2];
[myArray addObject:myObj3];
and So on ....
When you need the stored info you could use as below ...
for(int index =0; index < [myArray count]; index++)
{
MyViewDataHolder* myObj = (MyViewDataHolder*)[myArray objectAtIndex:index];
myView = [[UIView alloc] initWithFrame:myObj.mViewRect];
//incriment x and y to refelect where you want your next view to be suituated
myView.tag = myObj.mTag;
myView.backgroundColor = myObj.mDarkGrayColor;
[self.view addSubview:myView];
[myView release];
}
The code reflect the approach , Although I didn't compile the code So use it as reference
Thanks ,
You dont really need to store your views in an array unless its just for pure convenience.
You can create a variable amount views using a for loop:
UIView *myView;
int x=0, y=0;
int viewWidth = 50, viewHeight = 50;
for(int i=0; i<numberOFView; i++){
myView = [[UIView alloc] initWithFrame:CGRectMake(x,y,viewWidth,viewHeight)];
//incriment x and y to refelect where you want your next view to be suituated
myView.tag = i+1;
[self.view addSubview:myView];
[myView release];
}
Then to access each view in turn and carry out some work you can do the following:
for (UIView *view in self.view.subviews) {
switch (view.tag) {
case 1:
//do something to the view
break;
case 2:
//do something to the view
break;
case 3:
//do something to the view
break;
//and so on...
default:
break;
}
}
Related
I have a UIView that contains drop shadows and corners which I am loading four of in my UIViewController and I am seeing a performance hit when the screen loads. Since I am using the same white background with shadows and corner radii I figured I would store the UIView in NSCache.
When I run the app there is a large gap where the first UIView should be, however, it is not showing up. What does show up is the last view in my list of views. If I comment out the last one and run it again, the third one shows up. It seems like I am having an issue with the pointer in memory but not sure. Perhaps I am using NSCache incorrectly?
(Note: The first view shown is not using the NSCache)
Here is how I am using the NSCache:
.h file
#interface LunchDetailViewController : UIViewController <UIScrollViewDelegate>
#property (nonatomic) IBOutlet UIScrollView *scrollView;
#property (nonatomic, strong) NSCache *entreeViewsCache;
#end
.m file
#synthesize scrollView;
#synthesize entreeViewsCache;
- (void)viewDidLoad
{
[super viewDidLoad];
self.entreeViewsCache = [[NSCache alloc] init];
UIView *entreeView = [[UIView alloc] init];
entreeView.backgroundColor = [UIColor whiteColor];
entreeView.layer.masksToBounds = NO;
entreeView.layer.cornerRadius = 3.0;
entreeView.layer.shadowOffset = CGSizeMake(1.1, 2.1);
entreeView.layer.shadowOpacity = 0.2;
[self.entreeViewsCache setObject:entreeView forKey:#"EntreeView"];
}
- (void) configureScrollView
{
// This line of code allows the scroll view to be 'scrollable'.
self.scrollView.contentSize = CGSizeMake(320, 620);
UIView *elementaryRoundedCornerView = [self.entreeViewsCache objectForKey:#"EntreeView"];
elementaryRoundedCornerView.frame = CGRectMake(15,15,290,180);
UIView *middleRoundedCornerView = [self.entreeViewsCache objectForKey:#"EntreeView"];
middleRoundedCornerView.frame = CGRectMake(15,210,290,180);
UIView *highRoundedCornerView = [self.entreeViewsCache objectForKey:#"EntreeView"];
highRoundedCornerView.frame = CGRectMake(15,404,290,180);
NSMutableArray *entreeItems = [[NSMutableArray alloc] initWithObjects:#"Pancakes w/ Sausage Patties", #"Corn Dog", #"Grilled Cheese Sandwhich", #"Chicken Tender Wraps", nil];
UIView *elementaryLunchMenuDetails = [[UIView alloc] initWithFrame:CGRectMake(10, 10, 240, 160)];
[elementaryLunchMenuDetails addSubview:[self returnNativeCode:entreeItems rectDimensions:CGRectMake(2, 5, 215, 160) schoolType:#"Elementary"]];
[elementaryRoundedCornerView addSubview:elementaryLunchMenuDetails];
UIView *middleLunchMenuDetails = [[UIView alloc] initWithFrame:CGRectMake(10, 10, 240, 160)];
[middleLunchMenuDetails addSubview:[self returnNativeCode:entreeItems rectDimensions:CGRectMake(2, 2, 215, 160) schoolType:#"Middle"]];
[middleRoundedCornerView addSubview:middleLunchMenuDetails];
UIView *highLunchMenuDetails = [[UIView alloc] initWithFrame:CGRectMake(10,10, 240, 160)];
[highLunchMenuDetails addSubview:[self returnNativeCode:entreeItems rectDimensions:CGRectMake(2, 2, 215, 160) schoolType:#"High"]];
[highRoundedCornerView addSubview:highLunchMenuDetails];
[self.scrollView addSubview:elementaryRoundedCornerView];
[self.scrollView addSubview:middleRoundedCornerView];
[self.scrollView addSubview:highRoundedCornerView];
}
Wow. That's clever. But not correct.
Instead of using NSCache to duplicate a view, you probably want to create a UIView subclass that formats the view the way you want. Then just throw a bunch of those views on your scrollview.
ABCView.m
#implementation ABCDayView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = [UIColor whiteColor];
self.layer.masksToBounds = NO;
self.layer.cornerRadius = 3.0;
self.layer.shadowOffset = CGSizeMake(1.1f, 2.1f);
self.layer.shadowOpacity = 0.2f;
}
return self;
}
- (void)setItems:(NSArray *)items
{
if ([_items isEqualToArray:items] == NO) {
_items = items;
[self createItemViews];
[self setNeedsLayout];
}
}
// You'll also need to add -createItemViews and -setNeedsLayout methods.
.m file
- (void)configureScrollView
{
NSMutableArray *entreeItems = #[#"Pancakes w/Sausage Patties",
#"Corn Dog",
#"Grilled Cheese Sandwhich",
#"Chicken Tender Wraps"];
CGRect frame = CGRectMake(15,15,290,180);
ABCDayView *elementaryView = [[ABCDayView alloc] initWithFrame:frame];
elementaryView.items = entreeItems;
CGFloat y = CGRectGetMaxY(elementaryView.frame) + 10.0f;
frame = CGRectMake(15, y, 290, 180);
ABCDayView *middleView = [[ABCDayView alloc] initWithFrame:frame];
middleView.items = entreeItems;
...
CGFloat length = // Use CGRectGetMaxY on the last frame to get the length.
self.scrollView.contentSize = CGSizeMake(320, length);
}
That's by no means perfect code. But hopefully it will give you an idea of a better way to implement this.
So I have class called CSImageViews (subclass of uiview), that is essentially a row of UIImageViews enclosed in a single subclassed UIView.
I've added a custom init method like so (node/yaml contains uiimage name data):
- (id)initWithFrame:(CGRect)frame withNode:(NSDictionary *)node
{
self = [super initWithFrame:frame];
if (self) {
_node = node;
_imageViewYAML = [node objectForKey:#"items"];
_imageViews = [self getImageViewsForItems:_imageViewYAML];
}
return self;
}
And my getImageViewsforItems is like so (adds them all to subview):
-(NSArray *)getImageViewsForItems:(NSArray *)items
{
NSMutableArray *ivs = [NSMutableArray arrayWithCapacity:[items count]];
for (int i = 0; i < [items count]; i++) {
NSDictionary *item = [items objectAtIndex:i];
NSString *type = [item objectForKey:#"type"];
NSString *name = [item objectForKey:#"name"];
UIImage *image = [UIImage imageNamed:name];
UIImageView *iv = [[UIImageView alloc] init];
iv.image = image;
iv.tag = i;
[ivs addObject:iv];
[self addSubview:iv];
}
return ivs;
}
Now when I add this custom view to my main view like this nothing happens:
CSImageViews *imageViews = [[CSImageViews alloc] initWithFrame:frame withNode:node];
[view addSubview:imageViews];
But if I add this 'csimageviews' container into a new uiview first it appears:
CSImageViews *imageViews = [[CSImageViews alloc] initWithFrame:frame withNode:node];
UIView *v = [[UIView alloc] initWithFrame:frame];
[v addSubview:imageViews];
[view addSubview:v];
thoughts?
Stupid error by me. In this particular subclassed UIView I have a method that was called every time its parent view controller was shown. This method actually removed all subviews from the view... so I wasn't able to see the uiimageviews when I rendered it in the initial view... doh.
I am building an app to go alongside a web app I've built, which features a threaded comment system.
I'm wondering what the best approach to building this threaded view would be. Is there a relatively easy way to build an accordion style control?
I really like how the Alien Blue app does it, and the UI & UX is very smooth:
Does anyone have any idea how these are built?
Would making custom UIViews added as subviews be the best approach? If so, how would you implement 'collapse' style functionality?
I'd suggest creating a UIView subclass to contain each comment. It should have a toggle button to expand / collapse. On toggling open i'd set the frame size to the size of the content (plus any padding). It would contain an array of sub-comments, for each one you'd add the UIView subclasses to itself at expand time (And remove when collapsing) which would initially be collapsed (and so just appear as the toggle button). Collapsing is just the reverse, remove the subviews, set the frame to be the hight of the toggle button (plus padding)
As each comment view knows its size, you can put the whole thing in a UIScrollView with content-size set to the sum of the comment views sizes allowing scrolling regardless of expanded / contracted nature.
A partial implementation for this idea:
Comment.h
#import <Foundation/Foundation.h>
#interface Comment : NSObject {
NSMutableArray* subComments;
NSString* comment;
}
#property (nonatomic, retain) NSMutableArray* subComments;
#property (nonatomic, retain) NSString* comment;
#end
Comment.m
#import "Comment.h"
#implementation Comment
#synthesize comment, subComments;
-(id)init
{
self = [super init];
if (self)
{
subComments = [[NSMutableArray alloc] init];
}
return self;
}
#end
CommentView.h
#import <UIKit/UIKit.h>
#interface CommentView : UIView {
UIButton* toggle;
NSMutableArray* subComments;
NSString* commentText;
UITextView* comment;
BOOL expanded;
}
#property (strong, nonatomic) NSMutableArray* subComments;
#property (strong, nonatomic) NSString* commentText;
- (void) expand;
- (void) collapse;
- (void) toggleState;
#end
CommentView.m
#import "CommentView.h"
#implementation CommentView
#synthesize subComments,commentText;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
[self setBackgroundColor:[UIColor whiteColor]];
expanded = NO;
toggle = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
[toggle setTitle:#"Toggle" forState:UIControlStateNormal];
[toggle addTarget:self action:#selector(toggleState) forControlEvents:UIControlEventTouchUpInside];
CGRect curentFrame = self.frame;
curentFrame.size.height = toggle.frame.size.height + 10;
[self setFrame:curentFrame];
curentFrame = toggle.frame;
curentFrame.origin.y = 5;
curentFrame.origin.x = 5;
[toggle setFrame:curentFrame];
[self addSubview:toggle];
[self collapse];
return self;
}
- (void) toggleState
{
if (expanded)
{
[self collapse];
}
else
{
[self expand];
}
}
- (void) expand
{
comment = [[UITextView alloc] init];
[comment setEditable:NO];
[comment setText:commentText];
[self addSubview:comment];
CGRect curentFrame = comment.frame;
curentFrame.size.height = comment.contentSize.height;
curentFrame.origin.x = toggle.frame.size.width + toggle.frame.origin.x + 10;
curentFrame.size.width = self.frame.size.width - curentFrame.origin.x;
curentFrame.origin.y = toggle.frame.size.height + toggle.frame.origin.y + 10;
[comment setFrame:curentFrame];
curentFrame = self.frame;
curentFrame.size.height += comment.frame.size.height + 10;
[self setFrame:curentFrame];
float height = comment.frame.origin.y + comment.frame.size.height;
for (NSObject* o in subComments)
{
CommentView* subComment = [[CommentView alloc] initWithFrame:CGRectMake(comment.frame.origin.x,height,0,self.frame.size.width)];
[self addSubview:subComment];
height += subComment.frame.size.height;
curentFrame = self.frame;
curentFrame.size.height += subComment.frame.size.height;
[self setFrame:curentFrame];
[self bringSubviewToFront:subComment];
}
expanded = YES;
}
- (void) collapse
{
for (UIView* v in [self subviews])
{
[v removeFromSuperview];
}
CGRect curentFrame = self.frame;
curentFrame.size.height = toggle.frame.size.height + 10;
[self setFrame:curentFrame];
curentFrame = toggle.frame;
curentFrame.origin.y = 5;
curentFrame.origin.x = 5;
[toggle setFrame:curentFrame];
[self addSubview:toggle];
expanded = NO;
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
// Drawing code
}
*/
#end
ViewContoller.m (Example usage)
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
CommentView* mainCommentView = [[CommentView alloc] initWithFrame:CGRectMake(0, 0, 0, 0)];
Comment* mainComment = [[Comment alloc] init];
[mainComment setComment: #"Lorem Ipsum 1"];
Comment* sub1 = [[Comment alloc] init];
[sub1 setComment: #"Lorem Ipsum 1-1"];
Comment* sub11 = [[Comment alloc] init];
[sub11 setComment: #"Lorem Ipsum 1-1-1"];
[[sub1 subComments] addObject:sub11];
Comment* sub2 = [[Comment alloc] init];
[sub2 setComment: #"Lorem Ipsum 1-2"];
Comment* sub12 = [[Comment alloc] init];
[sub12 setComment: #"Lorem Ipsum 1-2-1"];
[[sub2 subComments] addObject:sub12];
[[mainComment subComments] addObject:sub1];
[[mainComment subComments] addObject:sub2];
[mainCommentView setCommentText:[mainComment comment]];
[mainCommentView setSubComments:[mainComment subComments]];
self.view = mainCommentView;
}
Looks like Apple has some sample code for collapsing UITableViewCells:
http://developer.apple.com/library/ios/#samplecode/TableViewUpdates/Introduction/Intro.html
There's also GCRetractableSectionController
Is something like this not just a webview with custom built HTML page displaying the comments?
here is what I wanna do http://www.youtube.com/watch?v=rD3MTTPaK98
I have done the part where an image appears at a random position but then it become hard for me. I don't wanna use UIKit Animations because I use a timer to animate like :imageView.center = CGPointMake(imageView.center.x + X, imageView.center.y + Y); but I can't animate more than one image at once I don't know why ? How can I solve this please
here is my code:
-(void) onTimer {
UIImage * image = [UIImage imageNamed:#"abouffer_03.png"];
imageView = [[UIImageView alloc] initWithImage:image];
[imageView setCenter:[self randomPointSquare]];
[[self view] addSubview:imageView];
[imageView release];
ix=imageView.center.x;
iy=imageView.center.y;
X=(240-ix)/230;
Y=(160-iy)/230;
coteA=(240-ix);
coteB=(160-iy);
angleDeRotation=atan(coteB/coteA);
if(ix>250||0>iy>320){
imageView.transform = CGAffineTransformMakeRotation(angleDeRotation+3.14);
}
else{
imageView.transform = CGAffineTransformMakeRotation(angleDeRotation);
}
}
-(void)onTimer2{
imageView.center = CGPointMake(imageView.center.x + X, imageView.center.y + Y);
label.text= [NSString stringWithFormat:#"%d", count];
Since your are not posting all of your code here, so I have to guess.
Are you creating imageView in onTimer and then trying to move "them" in onTimer2?
If YES, then
Because you are always setting the imageView var to the latest imageView you created in onTimer method, when you trying move the imageView in onTimer2 method, you are only moving the latest one you created.
In order to move every imageView you've created, you'd better have a for loop in your onTimer2 method.
In additional, you'd better make a NSMutableArray to save all your imageViews, or you can use imageView.tag to get your imageView again without having to create an NSMutableArray and release it later.
Here is an example how to use NSMutableArray to save and read all of your imageViews:
Add this to your class's .h file:
NSMutableArray *views;
NSMutableArray *XArray;
NSMutableArray *YArray;
Edit your .m file:
...
//Create an array some where in your class:
views = [[NSMutableArray array] retain];
XArray = [[NSMutableArray array] retain];
YArray = [[NSMutableArray array] retain];
...
- (void)onTimer {
[XArray addObject:[NSNumber numberWithFloat:(240.0f - ix)/230.0f]];
[YArray addObject:[NSNumber numberWithFloat:(160.0f - iy)/230.0f]];
UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"image.png"]];
[self.view addSubview:imageView];
[views addObject:imageView];
[imageView release];
}
//Read:
- (void)onTimer2 {
for (NSUInteger i = 0; i < [views count]; i++) {
UIImageView *imageView = [views objectAtIndex:i];
CGFloat X = [[XArray objectAtIndex:i] floatValue];
CGFloat Y = [[YArray objectAtIndex:i] floatValue];
CGPointMake(imageView.center.x + X, imageView.center.y + Y);
}
}
- (void)dealloc {
...
[views release];
[XArray release];
[YArray release];
...
}
Or you can use imageView.tag like this:
Add this to your class's .h file:
NSInteger imageViewCount;
Edit your .m file:
...
#define kImageViewFirstTag 10000
...
...
//reset the count some where in your class if needed
imageViewCount = 0;
...
- (void)onTimer {
UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"image.png"]];
[self.view addSubview:imageView];
[views addObject:imageView];
imageView.tag = kImageViewFirstTag + imageViewCount;
imageViewCount ++;
[imageView release];
}
//Read:
- (void)onTimer2 {
for (NSUInteger i = 0; i < imageViewCount; i++) {
UIImageView *imageView = (UIImageView *)[self.view viewWithTag:kImageViewFirstTag + i];
CGPointMake(imageView.center.x + X, imageView.center.y + Y);
}
}
The two code are compiled by brain, correct me if there is any mistype.
The first way is recommended, because it has better performance.
You're overwriting your ivar imageView each time -onTimer is called. You should probably put each created imageview in an array or a set, or you could depending on your view structure iterate over self.view.subviews.
I used the code to try set the label text but its not working. I though maybe I forgot to connect the outlet with the label, or i connected the wrong outlet but they were ok once I checked them. Made sure the xib was saved.
.m
#synthesize nameLabel;
#synthesize infoLabel;
-(void) updateUI
{
nameLabel.text = #"test1";
infoLabel.text = #"test2";
}
.h
UILabel * nameLabel;
UILabel * infoLabel;
#property(nonatomic, retain) IBOutlet UILabel *nameLabel;
#property(nonatomic, retain) IBOutlet UILabel *infoLabel;
Thats pretty much all the code used in the veiw controller related to these labels. Is there something i am missing that might explain this strangeness?
The default text i have in the labels 'name' & 'info' is what is showing.
This is the code that gets called prior to updateUI being called
browseDeckViewController.m
-(void) viewDidLoad
{
cardOnTopOfDeck = 0;
cardSecondFromTopOfDeck=1;
deck = [[Deck alloc] init];
[deck loadDeckData];
Card *mySecondCard = [[Card alloc] init];
mySecondCard = [deck.deckArray objectAtIndex:cardSecondFromTopOfDeck];
secondCard = [[CardViewController alloc] initWithNibName:#"CardViewController"
bundle:[NSBundle mainBundle] numberOfStats:kNumStats];
[secondCard setCard:mySecondCard];
CGRect frame = secondCard.view.frame;
frame.origin.x = (320-frame.size.width)/2;
frame.origin.y = 10;
secondCard.view.frame = frame;
[self.view addSubview:secondCard.view];
topCard = [[CardViewController alloc] initWithNibName:#"CardViewController"
bundle:[NSBundle mainBundle] numberOfStats:kNumStats];
Card *myTopCard = [[Card alloc] init];
myTopCard = [deck.deckArray objectAtIndex:cardOnTopOfDeck];
[topCard setCard:myTopCard];
frame = topCard.view.frame;
frame.origin.x = (320-frame.size.width)/2;
frame.origin.y = 10;
topCard.view.frame = frame;
[self.view addSubview:topCard.view];
}
CardViewController.m
-(void) setCard:(Card *)newCard
{
[card release];
card = [newCard retain];
[self updateUI];
}
-(void) updateUI
{
NSLog(#"updateUI");
nameLabel.text = #"test1";
infoLabel.text = #"test2";
}
Your CardViewController is created and then you immediately try to set the text of a UILabel in that controller's view. The problem is, the view isn't loaded yet. At the time updateUI is called, viewDidLoad: has not yet been called for the CardViewController, which means nameLabel and infoLabel are nil.
You have to force the CardViewController's view to load from the NIB before trying to access any of the outlets. a simple [self view]; would suffice. For example:
-(void) updateUI {
NSLog(#"nameLabel's value: %#",nameLabel); //nil here
[self view];
NSLog(#"nameLabel's value after loading view: %#",nameLabel); //Now it's loaded
nameLabel.text = #"test1";
infoLabel.text = #"test2";
}
EDIT:
Another solution would be to move the call to updateUI to after addSubview:
myTopCard = [deck.deckArray objectAtIndex:cardOnTopOfDeck];
[self.view addSubview:topCard.view];
[topCard setCard:myTopCard];
frame = topCard.view.frame;
frame.origin.x = (320-frame.size.width)/2;
frame.origin.y = 10;
topCard.view.frame = frame;
This code is incorrect:
nameLabel.text = "test1";
infoLabel.text = "test2";
You are assigning char* pointers to an NSString* variable; you need to prefix the string with the # symbol.
Assuming that the updateUI method is called, nameLabel and infoLabel both exist when it is called, this should work:
nameLabel.text = #"test1";
infoLabel.text = #"test2";