I would like to create a 4 x 6 grid of UIImageViews that each contain a slightly different image. I would also like to be able to randomly select one of the instances and change it's image.
My question is what's the best way to set up the UIImageViews in a grid formation, perform a few actions between each setup, and randomly pick 1 of the 24 instances once setup is complete. Optimally, I wouldn't have to set up each one by one.
Thanks in advance.
There are different approaches you can take, depending on whether or not you want to use Interface Builder to layout your grid.
One option is to layout your 24 UIImageViews as subviews of a common parent view within IB. On the View Attributes tab you can set a "Tag" number from 1 to 24 to differentiate your UIImageViews. Then in your code you can use [parentView viewWithTag:tagNumber] to access each UIImageView.
If you prefer to do things more programmatically, you could create each of your UIImageViews in a loop in the loadView method of your UIViewController subclass. You could maintain an array (or an array of arrays corresponding to rows and columns) as a property of your controller, and store a reference to each of these image views as you create them. For each UIImageView you create, set its imageView.frame property to define its position, then call [view addSubview:imageView] to add it to the parent view.
I would do it programmatically for your sake.
NSArray *myViews = //I assume you can create an array of views
for (int i=0; i<rows; ++i) {
for (int j=0; j<columns; ++j) {
UIImageView *thisImageView = [myViews objectAtIndex:(i*columns+j)];
CGSize size = thisImageView.image.size;
[thisImageView setFrame:CGRectMake(j*size.width, i*size.height, size.width, size.height)];
[self.view addSubview:thisImageView];
}
}
//Later to pick one randomly
UIImageView *myRandomView = [myViews objectAtIndex:(arc4random()%[myViews count])];
[myRandomView setImage:myNewImage];
Related
I have two questions regarding the placement and handling UILabel's and UIImageView's:
a. I'd like to know if its possible to detect the location of the "edge" of the text in a UILabel, to put a UIImageView a certain distance to the left or right of the UILabel (with the same height), for example: (UILabel -certain distance in width- UIImageView). (Preferably a method that could be used to detect the edges of a UIImageView as well)
b. I'd also like to know how the creation of UILabel's and UIImageView's programmatically works, I understand that to create for example a UIImageView it's as simple as:
UIImageView *myImgView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 0, 0)];
but I'd like to know: If I have a method to create a UIImageView programmatically, and it sets the CGRect based on a factor that changes, would I need a new name for every UIImageView I create in code, or could I use the same name and add them every time the method runs by calling addSubView? If I do need to individually name them all, and I'm unsure how many I will need to create (because it depends on the user), what is the best method to go about this? If I can create them all with the same name, will they all respond to the same name? Example, if I set myImgView to hidden, will this effect all the UIImageView's named myImgView?
You seem to be new to object oriented programming (as evidenced by the last part), and I would highly recommend you read this: Object-Oriented Programming with Objective-C
But I will answer your question about the UIViews. UIImageView and UILabel are both subclasses of the UIView class. UIView has a special kind of variable called a property. That means that the variable can be set on an instance of that class by another class and can then be accessed from other classes as well. UIView has the frame property that contains the outline of the view in a CGRect. When you have a bit of code like
ImageView *myImgView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 0, 0)];
You are creating an instance of the UIImageView class, allocating memory for it, and initializing it with a frame (in this case you are using rect with zero height and width, which is a bit useless). In order to set the frame to something useful, you use the format CGRectMake(originX, originY, width, height). Keep in mind that on iOS, the origin is on the upper left hand corner of the view, and a higher y value will be farther down the screen. If you need to change the frame later, rather than creating a whole new instance, you can simply alter the property on the existing instance:
myImgView.frame = CGRectMake(10, 20, 50, 100);
//this will make the frame of myImgView to a rectangel 10 units off of the right side of the view, 20 units down from the top of the view, and have a width of 50 units and a height of 100 units
However, you have declared myImgView as a local variable, meaning that you can only access it in the function that you have declared it in, at the time that you have declared it. In order to access it anywhere and anytime, you will need to declare it as an Instance Variable, meaning that you declare it in the .h file.
#interface MyView : UIView {
UIImageView *myImgView;
}
#end
Then the variable will be accessible anywhere in that class. Altering its frame as I demonstrated above will alter the frame in the view.
Finally, you can get the frame of an view if you need to do calculations with it. For example, you can get the frame of myImgView like so:
CGRect viewFrame = myImgView.frame;
CGPoint origin = myImgView.frame.origin;
CGSize width = myImgView.frame.size;
float x = myImgView.frame.origin.x;
float height = myImgView.frame.size.height;
//I think you can get it from here
Now with UILabels, it can be a bit trickier. The frame for a UILabel does not necessarily match the frame that the text takes up. Usually the actual frame is a bit smaller. In order to get the frame for the text of a UILabel, use something like this:
//UILabel *myLabel has been declared either in the .h or earlier in the function
CGRect textFrame = CGRectMake(myLabel.x, myLabel.y, [myLabel.text sizeWithFont:myLabel.font].width, [myLabel.text sizeWithFont:myLabel.font].height);
Now, finally, to put it all together, this would be how to calculate a new frame for your UIImageView:
myImgView.frame = CGRectMake(myLabel.x + [myLabel.text sizeWithFont:myLabel.font].width, yOrigin, width , height);
//where myImgView is your UIImageView myLabel is your UILabel, yOrigin is the y value of the origin of the imageView, and width and height are the width and height of the image.
Well I hope that helps and I highly suggest you read the object oriented programming guide.
I need one long scrollable view with 2 thumbnails (wallpapers) in every row.
Every thumbnail has also a button to share and download photo, and when you click the thumbnail it shows full size wallpaper. So I was thinking about making a custom view (200x200) which will contain thumbnail and two buttons, and add them dynamically to scroll view depending how many wallpapers are on server.
Something like this:
NSUInteger i;
int xCoord=0;
int yCoord=0;
int thumbnailWidth=200;
int thumbnailHeight=200;
int buffer = 10;
for (i = 1; i <= [items count]; i++)
{
UIView *aView = [[ThumbnailView alloc] initWithThumnailAtIndex: i ];
aView.frame = CGRectMake(xCoord, yCoord, thumbnailWidth, thumbnailHeight );
[scrollView addSubview:aView];
xCoord += thumbnailWidth + buffer;
yCoord += thumbnailHeight + buffer;
}
[scrollView setContentSize:CGSizeMake(700, yCoord)];
Do you think that is a good way to do this?
Also, how would I handle lazy loading of thumbnails if there are a lot of thumbnails in scrollview?
As everybody else here has said, use a UITableView. I will add that you should use a custom UITableViewCell subclass. Create the class, then use Storyboards to make a table view scene with prototype cells. Set the class of the prototype cell to your cell subclass. Add as many imageview, labels, buttons, whatever nonsense you want to the prototype cell on the storyboard, then hook those up to your custom class using IBOutlets. Now when it's time to display something in the cell, just pass an object or dictionary containing all the info that's needed to the custom cell subclass, and have it populate the various view via the outlets. Here's a tutorial: http://www.techotopia.com/index.php/Using_Xcode_Storyboards_to_Build_Dynamic_TableViews_with_Prototype_Table_View_Cells
I would suggest you use the UITableView with custom UITableViewCell implementations. The tableview will automatically handle the recycling of the cells, so you would not need to worry as much about performance.
You could pre-load all your assets during the view initialization and then simply use them in the tableview's datasource.
I would use a UITableView and each cell would contain both images. Take a look at this answer.
About loading the images, try using EGOImageLoader, is very simple and effective. Here is the Github project.
I have 25 UIControls and 10 UIImageView. It looks like this:
Now the images are the UIImageVIews and the grids are the UIControls. What I want is when the user taps at a image and then taps at any of the grids (any blank grids of-course) the UIImageView is then removed from the current superView and added as the subView of the blank grid tapped. For example, if the user taps at the A1 grid first and then in the B2 grid, two actions occur,
UIImageView from A1 grid is removed,
UIImageView is added to B2 grid
This means when the user taps at the image at A1 grid and then taps at the B2 grid, the output window looks like this:
EDIT
So, at a time I need to have track for two UIControls:
1.which one is pressed first, and
2.which one is pressed second. And then remove the UIImageView from the first UIControl and add it to the second UIControl. I have tried giving the UIControls a tag like,
//....
A1.tag = 1;
//...
-(void)a1ViewTapped:(id)sender
{
int i = A1.tag;
[self switch:i];
}
//...
-(void)switch:(int)_tag
{
//....
UIView *view = (UIView*)[self.view viewWithTag:_tag];
[view removeFromSuperview];
//...
}
By this I can track a single UIControl, but need to track two of them.
How can I accomplish this, can anyone help?
UIControl inherits from UIView, so you can add a subview to your UIControl by calling addSubview:.
You can remove your UIImageView from the previous control by calling removeFromSuperview on the UIImageView
edit:
A very simple implementation would be to create a property that holds a reference to the selected UIControl. When the property is nil, then you are in selection mode. Touching up inside a control puts a reference to the control into the property. When the property is not nil, you are looking for a grid control in which to place the image.
You'll also need a way to keep track of whether your UIControl has one of the UIImageView instances as a subview. There are a number of ways to do that. One way would be to subclass UIControl to add a property to it that points to the UIImage it contains. Another way might be to simply examine the subviews of the UIControl to see if it contains one of the images in question. Another way would be to create some data structure to keep track of the positions of the images. How you do it is up to you, and the best way depends on the specifics of your implementation.
Just for the sake of discourse, let's assume you set up an NSMutableDictionary called imageMap that uses the tags of your UIControls as keys and sets references to the images as values. Then you could do something like:
-(void)selectGridControl:(UIView*)view
{
if (selectedControl == nil) // In this case nothing is selected
{
selectedControl = view;
}
else // In this case we are moving the image to the new view
{
UIImageView *selectedImage = [imageMap objectForKey:selectedControl.tag];
if (selectedImage != nil)
{
[selectedImage removeFromSuperview];
[view addSubview:selectedImage];
[imageMap removeObjectForKey:selectedControl.tag];
[imageMap addObject:selectedImage forKey:view.tag];
selectedControl = nil;
}
}
Initializing imageMap and handling cases when the destination control already has an image are left as exercises for the reader.
I am trying to change the look of the tableviewcells by adding columns. I know the normal tableview doesn't allow this. Can someone point me in the right direction to achieve something similar to the attached image? The basic functionality is to have a grid of cells. 3x3 and any of the 9 cells can be clicked and it opens another page. its similar to the image picker but instead of images it would be cells with subtitles etc.
http://www.bronron.com/apps/IpadMenu.jpg
Any ideas?
Thanks in advance.
I would recommend that you try using a UIScrollView instead of a UITableView for this.
Each item in your menu can be a UIImageView, or a UIButton which you add as subviews to your UIScrollView.
The following code can be insterted in viewDidLoad inside your view controller and assumes that you have setup your UISCrollView in interface builder, otherwise you would need to allocate your scroll view inside viewDidLoad.
The code will add a variable number of menu items to a UIScrollView with two columns. To deal with user input you can make all the menu items UIButtons which call associated IBAction methods, or in this case the menuItems are you views, so you can identify which menu item the user is touching by looking at the users touch location within the scroll view, and then carrying out the appropriate action based on this.
UIImageView *menuItem = nil;
//The x and y view location coordinates for your menu items
int x = 0, y = 0;
//The number of items you want in your menu
int numOfItemsToAdd = 10;
//The height and width of your menu items
int menuItemHeight = 50, menuItemWidth = 50;
//The content seize needs to refelect the number of menu items that will be added
//The hieght of the ocnten size is calclutated by multiplying the menu item height by the number of
//menu items devided by the number of menu items that fit across in the width of the view.
[scrollView setContentSize:CGSizeMake(320, menuItemHeight*numOfItemsToAdd];
for(int i=0; i<numOfItemsToAdd; i++){
if(i%2 == 0){//% the number of columns you want in your menu
x = 0;
if(i!=0)
y += menuItemHeight;
}else{
x = menuItemWidth;
}
menuItem = [[UIImageView alloc] initWithFrame: CGRectMake(x, y, menuItemWidth, menuItemHeight)];
//set the center of the menu item in the scroll superviews coordinate system
menuItem.center = CGPointMake(x, y);
//Add the name of the image you want for the menu item
//These strings could be stored in an array and retrieved in order
menuItem.image = [UIImage imageNamed:#"MyImage"];
//Finaly add the menu item to the scorll view
[scrollView addSubview:menuItem];
}
You can go for multi column grid table view layout , where you can define each and every row and column.
you can check this tutorial for Drawing a Grid in a iPhone UITableView – Tabular Cell Data
http://www.iphonedevx.com/?p=153
This is a completely custom view layout and IMHO, not really suitable for a table view. As with anything custom there are many ways to achieve what you want. Here's how I would approach this implementation myself. There may very well be some good open source implementations out there.
GridViewController : UIViewController
This is the top most view controller and has a UIScrollView containing 1 or more pages of GridView objects. This view controller would communicate with a dataSource that provides 0..N objects to display on the individual grid views.
GridView : UIView
Each grid view would be a custom UIViewSubclass that knows how to layout 0 to N objects as dictated by the data provided by the GridViewController dataSource. Each icon in the grid would be an appropriate UIView subclass, maybe UIButton or UIImageView or maybe custom depending on what it needs to look and act like.
More behavior would be added as needed to drag icons around, create new pages, persist state in the data model, etc etc.
I want to add around 80 imageview on my view and then later on want to change their images very frequently(multiple times per second, working on an audio meter). I have added images using following code:
-(void)drawAudioMeter {
UIImageView *imgvw = nil;
int x = 1;
for(int i = 0; i<80; i++) {
imgvw = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"line_dull.png"]];
imgvw.tag = i+100;
imgvw.frame = CGRectMake(x, 400, 3, 30);
x = x + 4;
[self.view addSubview:imgvw];
[imgvw release];
}
}
Now how will I change the images in the imageviews as I am not having any name for the imageviews. Using a for loop every second on all the images does not look as feasible option to me. Can some one point me out how should I do it?
You can use viewWithTag to get a specific image by its tag
UIIMageView *image = [UIView viewWithTag:1];
viewWithTag: Returns the view whose
tag property contains the specified
integer value.
(UIView *)viewWithTag:(NSInteger)tag Parameters tag The tag value to search
for. Return Value The view in the
receiver’s hierarchy whose tag
property matches the value in the tag
parameter.
Discussion This method searches the
current view and all of its subviews
for the specified view.
Availability Available in iOS 2.0 and
later. See Also #property tag
Related Sample Code GenericKeychain
SeismicXML Declared In UIView.h
taken from: http://developer.apple.com/library/ios/#documentation/uikit/reference/UIView_Class/UIView/UIView.html
Create array of UIImageView's in your .h file
UIImageView* imgvw[80];
Create the imgvw's in a forloop in viewDidLoad.
And remove creating and releasing of image views in your for loop. And so, in your drawAudioMeter, you can just get the object of your image view in the for loop and change the images accordingly.
I think it's not good idea to draw audio meter via 80 UIImage's ... Maybe you should look at SpeakHere example from Apple how they do this. Open Xcode, open help (option-cmd-?) and search for SpeakHere. It's an example which does record / playback audio with audio meter.