I'm trying to use a UIScrollView with UIViews (to manage them as xib files), for now I have an array where I store the views.
testingView is a UIViewcontroller with his own xib file and testingView2 is also an UIViewcontroller with his own different xib file
- (void) viewDidLoad {
[super viewDidLoad];
_testingView = [[TestingViewController alloc] init];
_testingView2 = [[TestingViewController2 alloc] init];
self.array = [[NSArray alloc] initWithObjects: _testingView.view, _testingView2.view, nil];
for (int i = 0; i < [array count]; i++) {
CGRect frame;
frame.origin.x = self.scrollingView.frame.size.width*i;
frame.origin.y = 0;
frame.size = self.scrollingView.frame.size;
[self.scrollingView addSubView: [self.array objectAtIndex:i]];
}
self.scrollingView.contentSize = CGSizeMake (_scrollingView.frame.size.width*[array count], _scrollingView.frame.size.height);
}
the problem is that he is taking only the first view (even if I switch them) and the second view is not shown, the same with UIImages instead of views is working, can someone explain me why? seems like he is not seeing the second view in the array order...
Why are you calculating the variable "frame" inside the for loop when you are not using it?
In my view you should make the following change to your code.
for (int i = 0; i < [array count]; i++) {
CGRect frame;
frame.origin.x = self.scrollingView.frame.size.width*i;
frame.origin.y = 0;
frame.size = self.scrollingView.frame.size;
**UIView *v=[self.array objectAtIndex:i];**
**v.frame=frame;**
**[self.scrollingView addSubView:v];**
}
Hope it works.
In the for loop, you calculate frame for newly added subview, but you don't set that frame to that subview.
In your loop, add your subview like this for example:
UIView *subview = (UIView *)[array objectAtIndex:i];
subview.frame = frame;
[self.scrollingView addSubView:subview];
You need to set the frame for each of the subviews in the scrolling view. For example, you can set the frame of _testingView2.view to be the offset of x from the _testingView1.view frame width.
_testingView2.view.frame = CGRectOffset(_testingView2.frame, CGRectGetWidth(_testingView1.frame), 0)
There are many ways to calculate the frame. Try to use the methods CGRectWidth, CGRectGetMaxX, etc.
Related
Perhaps I am thinking about this incorrectly, so please point me in the right direction..
I want to make many UIViews as a property so I can update them with different methods. Something like this:
#property UIView *view1
#property UIView *view2
...
#property UIView *view200
-(void)updateViews{
for (i=0; i <200; i++){
//Update view here
}
So my questions are:
How can I easily make multiple views?
How can I easily pass a message to each individual view?
So many great responses, I think I need to add a little more info.
Have tried putting my UIViews into an array to index them but I struggle to access them to update their positions within the UIViewController. I thought making them a property is the correct way to since I need to update them with the device heading. Looks like using a tag might be best. Can someone further comment on this approach:
for (i = 0 ; i < 144; i++) {
UIView *sunView =[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"sun.png"]];
sunView.tag = i;
}
//then later, in a separate method do the following:
for (i = 0 ; i < 144; i++) {
CGFloat CGx = self.acceleration.X;
CGFloat CGy = self.acceleration.Y
[sunView.tag.i setFrame:CGRectMake(CGx, CGy, 20, 20)]
}
Im not sure how to access the UView with the corresponding indexed tag. Can someone help me there too?
When they are really subviews then you could iterate through the superview's subviews array.
You could do that recursievely in order to apply it on all subivews of your subviews, if you have multiple hierarchy levels.
If you don't want to apply this to each of your superview's subviews, then tag your subviews with something like view.tag = 6502 and use that to identify those views to which you want to apply it.
Or maintain your own array or set (iterable collection) of views and iterate through them.
Create your views in a loop and store them in an NSMutableArray. That way you will have access to all of them in your view controller.
Create a property.
#property (monatomic) NSMutableArray *arrayOfViews;
self.arrayOfViews = [[NSMutableArray alloc] init];
UIView *view;
for (int i = 0; i < 10; i++)
{
view = [[UIView alloc] init];
[self.arrayOfViews addObject:view];
}
Run a loop over this array and access the views one by one and call the method you need to call.
On a side note: Why are you creating 200 UIViews? That's a lot :)
I think you need something like that:
-(void)updateViews
{
UIView *views;
UILabel *label;
for(int i = 0 ; i < numberOfViews ; i++)
{
views = [[UIView alloc] initWithFrame:CGRectMake(...)];
label = [[UILabel alloc] initWithFrame:CGRectMake(0,0,views.frame.size.width,views.frame.size.height)];
[label setText:[NSString stringWithFormat:#"%d",i]];
[views addSubview:label];
[self.view addSubview:views];
}
}
How can I easily make multiple views?
Do you want to create it in IB?
1) You can use tags in Xcode
UILabel * label = (UILabel*)[self.view viewWithTag:19];
UIView * view = (UIView*)[self.view viewWithTag:20];
2) You can use property that you also do
How can I easily pass a message to each individual view?
if your views inside another view(currentView) you can use that
for (UIView *yourView in self.currentView.subviews) {
//Update view here
}
Or if you want to create it in the code
for (int i = 0; i < 10; i++)
{
UIView * myView = [[UIView alloc]initWithFrame:CGRectMake(10*i, 10*i, 25, 25)];
int r = arc4random() % 255;
int g = arc4random() % 255;
int b = arc4random() % 255;
myView.backgroundColor = [UIColor colorWithRed:r/255.0 green:g/255.0 blue:b/255.0 alpha:1];
[self.view addSubview: myView];
}
That's what you can see
And to call all these views you can use
for (UIView *yourView in self.view.subviews) {
[yourView removeFromSuperview];
}
And that's what you'll see
If you want to call own view you can use tags like this
for (int i = 0; i < 10; i++)
{
UIView * myView = [[UIView alloc]initWithFrame:CGRectMake(10*i, 10*i, 25, 25)];
myView.tag = i + 21;
int r = arc4random() % 255;
int g = arc4random() % 255;
int b = arc4random() % 255;
myView.backgroundColor = [UIColor colorWithRed:r/255.0 green:g/255.0 blue:b/255.0 alpha:1];
[self.view addSubview: myView];
}
for (UIView *yourView in self.view.subviews) {
if (yourView.tag == 21) {
[yourView removeFromSuperview];
}
}
I made a view which holds a UIScrollview:
self.scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(10, 65, 300, 188)];
//BackViews will hold the Back Image
BackViews = [[NSMutableArray alloc] init];
for (int i=0; i<BigPictures.count; i++) {
[BackViews addObject:[NSNull null]];
}
FrontViews = [[NSMutableArray alloc] initWithCapacity:BigPictures.count];
[self.pageControl setNumberOfPages:BigPictures.count];
Then I add several UIImageviews containing images:
//BigPictures holds objects of type UIImage
for (int i = 0; i < BigPictures.count; i++) {
UIImageView *ImageView = [[UIImageView alloc] initWithImage:[BigPictures objectAtIndex:i]];
ImageView.frame = [self.scrollView bounds];
[ImageView setFrame:CGRectMake(self.scrollView.frame.size.width * i, ImageView.frame.origin.y, ImageView.frame.size.width, ImageView.frame.size.height)];
//this saves the FrontView for later (flip)
[FrontViews insertObject:ImageView atIndex:i];
[self.scrollView addSubview:test];
}
// Detect Single Taps on ScrollView
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(flip)];
[self.scrollView addGestureRecognizer:tap];
self.scrollView.contentSize = CGSizeMake(self.scrollView.frame.size.width * BigPictures.count, self.scrollView.frame.size.height);
self.scrollView.pagingEnabled = YES;
self.scrollView.delegate = self;
Ok so far so good. Now the method which does the flipImage part:
- (void)flip {
int currentPage = self.pageControl.currentPage;
UIView *Back = nil;
if ([BackViews objectAtIndex:currentPage] == [NSNull null]) {
//CreateBackView is just creating an UIView with text in it.
Back = [self CreateBackView];
[BackViews replaceObjectAtIndex:currentPage withObject:Back];
[UIView transitionFromView:[[self.scrollView subviews] objectAtIndex:currentPage] toView:[BackViews objectAtIndex:currentPage] duration:0.8 options:UIViewAnimationOptionTransitionFlipFromLeft completion:NULL];
} else {
[UIView transitionFromView:[[self.scrollView subviews] objectAtIndex:currentPage] toView:[FrontViews objectAtIndex:currentPage] duration:0.8 options:UIViewAnimationOptionTransitionFlipFromRight completion:NULL];
[BackViews replaceObjectAtIndex:currentPage withObject:[NSNull null]];
}
[self.view addSubview:Back];
[self rebuildScrollView];
}
This is what rebuildScrollView does:
- (void)rebuildScrollView
{
for (UIView *subview in self.scrollView.subviews) {
[subview removeFromSuperview];
}
for (int i = 0; i < BigPictures.count; i++) {
if ([BackViews objectAtIndex:i] == [NSNull null]) {
[self.scrollView addSubview:[FrontViews objectAtIndex:i]];
} else {
[self.scrollView addSubview:[BackViews objectAtIndex:i]];
}
}
self.scrollView.contentSize = CGSizeMake(self.scrollView.frame.size.width * BigPictures.count, self.scrollView.frame.size.height);
self.scrollView.pagingEnabled = YES;
self.scrollView.delegate = self;
}
So the behavior is the following:
If I click on the first image (1 of 3 in scrollview) the effect is the way I want it, meaning the frontimage turns around and shows the empty (white) back with some text in the middle
If I click on the second image, the image turns but the back is completely empty showing the grey background of the window. If I scroll to the other images, the still show the front image (as expected)
Now I click on the third image and its the same as 1) great.
Current layout is now [BackView, Nothing, Backview)
Lets run that again. But now I click on the last image and its the same as 2) :(
Any ideas whats going wrong here?
EDIT: Some new findings. I doubled the amount of pictures and this is how Front and Backviews are placed (after flipping each one). P = Picture & B = Backview.
P_1(B_1) - actually the only correct one
P_2(empty - should be B_2)
P_3(B_2 - should be B_3)
P_4(empty - should be B_4)
P_5(B_3 - should be B_5)
P_6(empty - should be B_6)
Did a complete rebuild and now it works. Really strange because I used the exact same code.
I'm trying to understand an issue I'm having with an iOS Universal app. I have a UIScrollView which I want the page of to take up the full dimension of the device and adjust itself when rotating.
In my viewDidLoad method, I'm testing with:
NSArray *colors = [NSArray arrayWithObjects:[UIColor orangeColor], [UIColor cyanColor], [UIColor redColor], [UIColor greenColor], [UIColor blueColor], nil];
for (int i = 0; i < colors.count; i++) {
CGRect frame;
frame.origin.x = _scrollView.bounds.size.width * i;
frame.origin.y = 0;
frame.size = _scrollView.bounds.size;
UIView *subview = [[UIView alloc] initWithFrame:frame];
subview.backgroundColor = [colors objectAtIndex:i];
[_scrollView addSubview:subview];
}
_scrollView.contentSize = CGSizeMake(_scrollView.bounds.size.width * colors.count, _scrollView.bounds.size.height);
then I added in the didRotateFromInterfaceOrientation method:
_scrollView.contentSize = CGSizeMake(_scrollView.bounds.size.width * colors.count, _scrollView.bounds.size.height);
for (int i= 0; i< colors.count; i++)
{
CGRect frame;
frame.origin.x = _scrollView.bounds.size.width * i;
frame.origin.y = 0;
frame.size = _scrollView.bounds.size;
UIView *subview= [[_scrollView subviews] objectAtIndex:i];
subview.frame= frame;
}
This seems to work, but I seem to have some clipping on the main view while the device is rotating.
Here's a screenshot of what happens when rotating:
I thought this happened because I changed the size after it was rotated, but I tried to handle it in the willRotateToInterfaceOrientation method, but it gave the wrong result...
Am I handling this incorrectly?
What would be the correct approach?
TIA!
S.
Am I handling this incorrectly?
You should put the code that changes the bounds in willRotateToInterfaceOrientation, but do so in an animation block (using [UIView animateWithDuration:0.4 animations:...])_so that the transition will be smooth rather than immediate.
An extra note is that inside willRotateToInterfaceOrientation, the parent view will not yet have been resized, so you cannot depend on it's bounds to calculate the final position of the views, rather you must do so manually.
I'm sure that I'm missing something terribly easy here...
I loop through the images just fine the first time, the indices reset and increment properly, but the UIView doesn't refresh and keeps the last image in the array showing. transitionFromView: automatically swaps the subviews, right? Do I need to implement the completion: option?
What am I missing? Here's the pertinent code:
- (id)initWithFrame:(CGRect)frame
{
NSInteger maxSlides = 12;
self = [super initWithFrame: frame];
if (self) {
//set up an array of images to be used for a flipbook
slides = [[NSMutableArray alloc] initWithCapacity: maxSlides];
for(NSInteger i=0; i<maxSlides; ++i) {
imageView = [[UIImageView alloc] initWithImage:
[UIImage imageNamed:[NSString stringWithFormat: #"muybridge%d.png", i]]];
[slides addObject: imageView];
}
index = 0; //default to first photo.
[self addSubview: [slides objectAtIndex: index]];
}
return self;
}
- (void) touchesEnded: (NSSet *) touches withEvent: (UIEvent *) event
{
//output indices for debugging. (yes, they increment properly)
NSLog(#"index = %d", index);
NSLog(#"currentIndex = %d", currentIndex);
NSLog(#"nextIndex = %d", nextIndex);
if (nextIndex <= 10) {
currentIndex = nextIndex;
} else {
currentIndex = 0;
}
nextIndex = currentIndex + 1;
//flipbook - tapping the image transitions to the next image in the array
[UIView transitionFromView: [slides objectAtIndex: currentIndex]
toView: [slides objectAtIndex: nextIndex]
duration: 0
options: UIViewAnimationOptionTransitionCurlUp
completion: NULL
];
}
What you're missing is this, from the docs on transitionFromView: toView:... :
fromView
The starting view for the transition. By default, this view is removed from its superview as part of the transition.
So it works, the first time through, but only one subview, the last one, remains after you've looked at them all. Look at the UIViewAnimationOptionShowHideTransitionViews option.
According to the Apple reference for UIView: transitionFromView: toView:... :
This method modifies the views in their view hierarchy only. It does not modify your application’s view controllers in any way. For example, if you use this method to change the root view displayed by a view controller, it is your responsibility to update the view controller appropriately to handle the change.
Maybe try calling the [yourView setNeedsDisplay] method right after.
.h file
#interface AppViewController : UIViewController {
...
UIImageView *imageViewArray[3][5];// not using any #property for this
...
}
...
#define FOR(j,q) for (int j = 0; j < q; j++)
.m file
ViewDidLoad{
FOR(i,5){
FOR(j,3)
{
image_names[0] = [UIImage imageNamed:[NSString stringWithFormat:#"%d.png",j]];
imageViewArray[j][i] = [[UIImageView alloc] initWithImage: image_names[0]];
CGRect newFrame ;
newFrame = CGRectMake(60+(j*130), 110+(150*i),130,136);
imageViewArray[j][i].frame = newFrame;
[self.view addSubview:imageViewArray[j][i]];
}
}
}
//clear the previous images and call a method to add new
-(IBAction)Change{
FOR(i,5)
FOR(p,3)
imageViewArray[p][i].image = nil;
[self ImageSwitch];
}
-(void)ImageSwitch{
FOR(i,5){
FOR(j,3)
{
image_namesTmp[0] = [UIImage imageNamed:[NSString stringWithFormat:#"%d.png",j+3]];
imageViewArray[j][i] = [[UIImageView alloc] initWithImage: image_namesTmp[0]];
CGRect newFrame ;
newFrame = CGRectMake(60+(j*130), 110+(150*i),130,136);
imageViewArray[j][i].frame = newFrame;
[self.view addSubview:imageViewArray[j][i]];
}
}
}
when i press the button for the first time it works fine(old images get replaced with new ones),but if i do it second time
imageViewArray[p][i].image = nil;
this line wont work and all the previous images still remain there and new images are overlapping with the present ones
In the ImageSwitch method you are always creating new instances of UIImageView.
imageViewArray[j][i] = [[UIImageView alloc] initWithImage: image_namesTmp[0]];
At this point, there already is a reference to UIImageView stored in the variable. You are losing your reference to it and since you alloc/inited it, you will most probably also leak its memory. Moreover, you are adding new UIImageViews to the view hierarchy, but you are not removing the old ones.
Try something like this:
[imageViewArray[j][i] removeFromSuperview];
[imageViewArray[j][i] release];
imageViewArray[j][i] = [[UIImageView alloc] initWithImage: image_namesTmp[0]];
Second time you set your images you don't need to recreate the UIImageView objects. Your ImageSwitch method is better declared as,
-(void)ImageSwitch{
FOR(i,5){
FOR(j,3)
{
imageViewArray[j][i].image = [UIImage imageNamed:[NSString stringWithFormat:#"%d.png",j+3]];
}
}
}
Just changing the images will suffice and that's what you need to do.
You need an NSMutableArray of ImageViews. In your header declare the NSMutableArray:
NSMutableArray *_imageViewArray;
Then in your viewDidLoad method initialize the array and then populate it with your image views.
_imageViewArray = [[NSMutableArray alloc]init];
for (int i =0; i < imageNames; i++) {
UIImageView *tempImageView = [[UIImageView alloc]initWithImage:[UIImage imageNamed:[imageNames objectAtIndex:i];
[_imageViewArray addObject:tempImageView];
[tempImageView release];
}
So thats the image view array populated, you may do it a different way but thats up to you.
To switch the views over what I would do is like so...
[self.view removeAllSubviews]; // gets rid of all previous subviews.
[self.view addSubview:[_imageViewArray objectAtIndex:3]];// could be any number. generate a random number if you want.
Thats all you would need to do. If you wanted to know which image view was being displauyed the maybe declare an instance of UIImageView in your header called _currentImageView and then you can remove just that image view instead of all subviews.
Hope this helped.