I'm having what I think is a bit of a newbie problem...
I've made a dynamic scroll view where images and labels are programmatically added as subviews. Problem is that only the last one I'm adding as a subview is showing.
Also when I read about "addSubview:" it says "Adds a view to the end of the receiver’s list of subviews." Does this mean only the last added subview is supposed show? In that case how do I make both visible?
Thanks in advance,
Tom
CODE:
for(int i = 0; i < [famorableArray count]; i++){
UIButton *famorableButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[famorableButton setFrame:CGRectMake(0.0f, 5.0f, 57.0f, 57.0f)];
[famorableButton setImage:personLogo forState:UIControlStateNormal];
NSString *famString = [NSString stringWithFormat:#"%#", [[[famorableArray objectAtIndex:i] substringFromIndex:8] capitalizedString]];
NSLog(#"%#", famString);
UILabel *famLabel = [[UILabel alloc] initWithFrame:CGRectZero];
famLabel.text = famString;
NSLog(#"Test2 %#", famLabel.text);
// Move the buttons position in the x-demension (horizontal).
CGRect btnRect = famorableButton.frame;
btnRect.origin.x = totalButtonWidth;
[famorableButton setFrame:btnRect];
CGRect labelRect = famLabel.frame;
labelRect.origin.x = totalButtonWidth + 28.5f;
[famLabel setFrame:btnRect];
// Add the button to the scrollview
[famScroll addSubview:famLabel];
[famScroll addSubview:famorableButton];
// Add the width of the button to the total width.
totalButtonWidth += famorableButton.frame.size.width + 30;
}
[famScroll setContentSize:CGSizeMake(totalButtonWidth, 79.0f)];
For each added subview you have to set the frame property. The frame is the view's position in the superview. "Adding to the list of subviews" does not imply any automatic layout. So I assume that all your subviews are visible but overlap.
My bad... It was a fail editing after copy paste. Accidentally used btnRect as a frame for the label too. Thanks for your effort anyways #Martin :)
Related
Unable to click the dots in Page Control. Also, need to change the UICollectionView while clicking the dots. Anyone help me regarding this?
-(void)changepage:(UIPageControl *)sender {
// UIPageControl *pager=sender;
page = sender.currentPage;
CGRect frame = self.AchievementCollection.frame;
frame.origin.x = frame.size.width * page;
frame.origin.y = 0;
[self.AchievementCollection scrollRectToVisible:frame animated:YES];
NSLog(#"page control value %li",(long)sender.currentPage);
}
in Cell For Item at Index Path
AchievementCollectionCell* cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"AchievementCollection" forIndexPath:indexPath];
[pageControls addTarget:self action:#selector(changepage:) forControlEvents:UIControlEventValueChanged];
cell.MiddleLabel.text = [[items objectAtIndex: indexPath.row]valueForKey:#"userCertificateName"];
cell.BottomLabel.text = [[items objectAtIndex: indexPath.row]valueForKey:#"userCertificateDesc"];
return cell;
}
I don't think you're supposed to let the dot clickable, since it's too small for a finger gesture. If you would like something like next / back, then you better have other 2 buttons to handle that.
But, IMHO, the best UX is still let user scroll the scrollview manually.
I have an array , data of which 'm displaying on scrollview using UILabel. I can scroll right and left. But I want to add one more feature in that which is automatic scrolling of the label text on scrollview. That means even if I do not scroll I want it to move by itself.
My code for scrolling is as below:
- (void)viewDidLoad
{
[super viewDidLoad];
_scrollView.contentInset=UIEdgeInsetsMake(0, 300, 0, 300);
NSMutableArray *items=[[NSMutableArray alloc] initWithObjects:#"A walk to remember",#"The Last song",#"The Notebook",#"Dear John",#"The Longest Ride",nil];
CGFloat y = 10;
self.label = [[UILabel alloc]initWithFrame:CGRectMake(0,y,800, 25)];
for(int i=0;i<[items count];i++){
y+=20;
NSString* lab=[items objectAtIndex:i];
_label.backgroundColor=[UIColor clearColor];
lab=[items componentsJoinedByString:#" | "];
NSLog(#"label:%#",lab);
[self.label setLineBreakMode:NSLineBreakByWordWrapping];
[self.label setText:lab];
}
NSTimer *timer = [NSTimer timerWithTimeInterval:1
target:self
selector:#selector(timer)
userInfo:nil
repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
//[_scrollView scrollRectToVisible:NSMakeRange(0,0,0.0) animated:YES];
[_scrollView addSubview:_label];
[UIView commitAnimations];
_scrollView.contentSize=CGSizeMake(_scrollView.frame.size.width*[items count], _scrollView.frame.size.height);
}
timer code:
-(void)timer {
[self.label setText:[NSString stringWithFormat:#" %#", self.label.text]];
}
Please help me how could I make the label text move horizontally. Thanks for any help
That's a fairly horrible way to implement scrolling if I may so so! You're scrolling by inserting incremental spaces to the left of the string. There is a much easier way to do this!
Keep your code that adds the UILabels to your scroll view
Get rid of the NSTimer stuff
Add a basic UIView animation to animate the scrollview's content offset.
Something like this:
[UIView animateWithDuration:0.3f delay:0.0f options:UIViewAnimationOptionCurveEaseInOut animations:^{
_scrollview.contentOffset = CGPointMake(-50.0f, 0.0f);
} completion:nil];
This will slide the whole scrollview left by 50 points over 0.3secs. You can play around with the settings.
btw, typically you'd create the UILabels in viewDidLoad (as you have it) but put the animation in viewWillAppear or viewDidAppear
Have had another look at your code. I thought you were adding multiple UILabels to your uiscrollview but actually you're adding just one with a long text string.
You can simplify your code to set up the label quite a bit, then implement the scrolling animation per the below.
NSMutableArray *items=[[NSMutableArray alloc] initWithObjects:#"A walk to remember",#"The Last song",#"The Notebook",#"Dear John",#"The Longest Ride",nil];
CGFloat y = 10;
self.label = [[UILabel alloc]initWithFrame:CGRectMake(0,y,800, 25)];
[self.label setLineBreakMode:NSLineBreakByWordWrapping];
[self.label setBackgroundColor: [UIColor clearColor]];
[self.label setText: [items componentsJoinedByString:#" | "]];
EDIT
To your question about how to show the whole line scrolling and then repeating, there are a couple of steps here.
Make the UILabel wide enough to take all the text on a single line - i.e. increase the width so that it's big enough to fit all the text. You can do this by trial and error or use (CGSize)sizeThatFits:(CGSize)size
to get the size. You call CGSize size = self.label sizeThatFits:CGSizeMake(CGFloatMax, self.label.frame.size.height) to get the size then apply this to the label's frame:
CGRect frame = self.label.frame;
frame.size.width = size.width; //calculated above with sizeThatFits
self.label.frame = frame; //update the frame
Now scroll the scrollview by the full width. So the animateWithDuration code would become:
_scrollview.contentOffset = CGPointMake(-size.width, 0.0f);
The final step is how to make it restart once it's got to the end. Not sure what effect you want here? Should it jump back to the start? (if so set the contentOffset x to zero), or continue scrolling? (if so, use a trick like adding a second UILabel at the end of the first.
BTW, if what you're after is a simple scrolling text like a banner on a webpage, then I suggest you don't bother with the scrollview at all. Instead add the UILabel to self.view and then animate the UILabel's frame origin directly.
I´ve been reading the different questions ( Cocoa: What's the difference between the frame and the bounds?, UIView frame, bounds and center) related to the difference between frame and bounds but still I don´t understand why when something like this:
UILabel *newMark = [[UILabel alloc] initWithFrame:self.frame];
newMark.text = #"|A3";
[self addSubview:newMark];
or this:
UILabel *newMark = [[UILabel alloc] init];
newMark.text = #"|A3";
newMark.frame = self.frame;
[self addSubview:newMark];
The label is not displayed, but when doing the equivalent with the bounds, like this:
UILabel *newMark = [[UILabel alloc] initWithFrame:self.bounds];
newMark.text = #"|A3";
[self addSubview:newMark];
or this
UILabel *newMark = [[UILabel alloc] init];
newMark.text = #"|A3";
newMark.frame = self.bounds;
[self addSubview:newMark];
it is displayed. I wouldn´t have a problem with using the bounds, but I think is not placing the label on the right place, as it can be seen on the following screenshot:
Where, as you can see "|", "A" and "3" are cut because they are displayed more at the bottom than the screen of the ipad. Any ideas?
The frame is relative to its superview. So if your frame's origin is at (100,200) within your superview, and you set a subview's frame to be the same as your frame, then your subview will be at (100,200) within you.
Typically (provided there aren't transforms involved), when you want to tell a subview to be the same size as the superview, you say:
subview.frame = superview.bounds;
I am attempting to create a "metro" styled UIScrollView. It is similar to how iTunes app handles panels in the new ios version which wont be named.
I can't figure out how to have my views layout/scroll so that the next view in the sequence shows up. I've tried all sorts of things like keeping the contentSize the screen width but moving each view over -10ish so it will show up like above. I've tried making scrollView whose bounds were smaller than the screen so it would show the part of the next view. Nothing works.
Here is diagram of what I'm trying to do:
It seems extremely trivial on paper but I can't seem to get it work.
I'm not sure if I'm misinterpreting your requirements - but this might be a starting point to see how you could set it up:
- (void)viewDidLoad
{
[super viewDidLoad];
CGRect viewBounds = self.view.bounds;
CGRect scrollViewFrame = CGRectMake(0, 0, floorf(CGRectGetWidth(viewBounds) / 2.2), CGRectGetHeight(viewBounds));
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:scrollViewFrame];
scrollView.center = self.view.center;
scrollView.contentSize = CGSizeMake(CGRectGetWidth(viewBounds) * 3, CGRectGetHeight(viewBounds) * 3);
scrollView.pagingEnabled = YES;
scrollView.clipsToBounds = NO;
UIPanGestureRecognizer *gestureRecognizer = scrollView.panGestureRecognizer;
[self.view addGestureRecognizer:gestureRecognizer];
for (int i = 0; i < 3; i++) {
CGRect frame = CGRectMake(10.f + (i * CGRectGetWidth(scrollView.bounds)), 10.f, CGRectGetWidth(scrollView.bounds) - 20.f, (CGRectGetHeight(scrollViewFrame) * 3) - 20.f);
UIView *view = [[UIView alloc] initWithFrame:frame];
view.backgroundColor = [UIColor redColor];
[scrollView addSubview:view];
}
[self.view addSubview:scrollView];
}
Literally just put this in an empty viewController's viewDidLoad:
The key things to note are
contentSize needs to be wide enough for all the panels
clipsToBounds should be NO so you can see the additional views
The bounds of the scrollview is essentially the main view port
pagingEnabled should be set
I've grabbed the panGestureRecognizer from the scrollview and attached it to the containing view instead so that panning is detected in the bounds of the containing view (which is larger) otherwise you are restricted to only detecting scrolls within the scrollviews bounds
I have a UIViewController class where I create a UIView instance.
And then I initialize (means fill) it with 25 subviews each containing an image (1, 2, ..., 25). Then after clicking 5 times in these image I called a function where I used
for(UIView *subview in [contentView subviews]) {
[subview removeFromSuperview];//ContentView name of my view
}
to remove the previously added subview. And then I use the same approch to
add 25 new subviews (image 1,2,3,....25). But this time no subview is added.
Can someone plz give me full code of adding & removing subview.
I have used the following code when I first add subview
//create main window
contentView = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
contentView.backgroundColor = [UIColor blackColor];
self.view = contentView;
[contentView release];
//adding 25 subview 1st time
int a=0;
int b=0;
for (int i = 0; i < 26; i++)
{
CGRect dragRect = CGRectMake(0.0f, 0.0f, x, y);
dragRect.origin = CGPointMake(a,b);
DragView *dragger = [[DragView alloc] initWithFrame:dragRect];
NSString *Flower = [[NSArray arrayWithObjects:#"1.png", #"2.png", #"3.png",#"4.png", #"5.png", #"6.png",#"7.png",#"8.png", #"9.png",#"10.png", #"11.png", #"12.png",#"13.png",#"14.png",#"15.png",#"16.png",#"17.png",#"18.png",#"19.png",#"20.png",#"21.png",#"22.png",#"23.png",#"24.png",#"25.png",#"26.png", nil] objectAtIndex:i];
[dragger setImage:[UIImage imageNamed:Flower]];
[dragger setUserInteractionEnabled:YES];
[self.view addSubview:dragger];
[dragger release];
a+=10;
b+=10;
}
//then removing 25 subview
//adding 25 subview 2nd times
I used the same approch to add the second time as first time, but the problem is that when I remove 25 subview and then add 25 subview, these subview are not added/shown, the view remain same. I am tired with these problem. plz someone help me.
The problem might be the way you remove the old views. You are modifying an array while iterating over it, which does not work. Try this code to remove the old views:
UIView* subview;
while ((subview = [[contentView subviews] lastObject]) != nil)
[subview removeFromSuperview];
A nice one liner is:
[view.subviews makeObjectsPerformSelector:#selector(removeFromSuperview)];
Looking at your code, I can suggest the following changes. There's one line in your for loop which is terribly inefficient:
NSString *Flower = [[NSArray arrayWithObjects:#"1.png", #"2.png", #"3.png",#"4.png", #"5.png", #"6.png",#"7.png",#"8.png", #"9.png",#"10.png", #"11.png", #"12.png",#"13.png",#"14.png",#"15.png",#"16.png",#"17.png",#"18.png",#"19.png",#"20.png",#"21.png",#"22.png",#"23.png",#"24.png",#"25.png",#"26.png", nil] objectAtIndex:i];
[dragger setImage:[UIImage imageNamed:Flower]];
Either take the Flower initialisation out of the the for loop (to only create the array once) or do the following:
[dragger setImage:[UIImage imageNamed:[NSString stringWithFormat:#"%d.png", i]]];
The code itself looks like it should work though. If you add 26 subviews then remove the 26 subviews then add the 26 subviews in exactly the same way then it should display as you'd expect.