How to add UIView to UIScrollView dynamically if previous UIView is full - ios

I have to do a pdf generator with custom label, so in my storyboard I have a UIScrollView with a UIView inside it, and if my UIView is full, I want to add another UIView and fill it with other label that is not already in. How can I add another page in my UIScrollView with another UIView ? I have to do it programmatically or there is an option in storyboard for doing it ?
I tried this (I don't know if it works and I don't want to compile it 'cause I find it really really ugly) :
CGFloat placement = titleLabel.frame.size.height + kMarginLabel;
for(int i = 0; i < [contract.nameContract count]; i++)
{
if(placement > _contractPageView.frame.size.height)
{
CGFloat secondPlacement = 0;
_scrollView.contentSize = CGSizeMake(_contractPageView.frame.size.width * 2, _contractPageView.frame.size.height);
UIView *secondPage = [[UIView alloc] initWithFrame:_contractPageView.frame];
articleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0 + kMarginLabel, secondPlacement, kTitleHeight, kTitleWidth)];
articleLabel.text = [NSString stringWithFormat:#"Article %i - %#",i, [contract.nameContract objectAtIndex:i]];
[secondPage addSubview:articleLabel];
secondPlacement += articleLabel.frame.size.height;
clauseLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, secondPlacement + kMarginLabel, _contractPageView.frame.size.width, kSizeOfClause)];
clauseLabel.text = [contract.textContract objectAtIndex:i];
secondPlacement += clauseLabel.frame.size.height;
[secondPage addSubview:clauseLabel];
[_scrollView addSubview:secondPage];
if(secondPlacement > secondPage.frame.size.height)
{
CGFloat thirdPlacement = 0;
_scrollView.contentSize = CGSizeMake(_contractPageView.frame.size.width * 3, _contractPageView.frame.size.height);
UIView *thirdPage = [[UIView alloc] initWithFrame:_contractPageView.frame];
articleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0 + kMarginLabel, thirdPlacement, kTitleHeight, kTitleWidth)];
articleLabel.text = [NSString stringWithFormat:#"Article %i - %#",i, [contract.nameContract objectAtIndex:i]];
[thirdPage addSubview:articleLabel];
thirdPlacement += articleLabel.frame.size.height;
clauseLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, thirdPlacement + kMarginLabel, _contractPageView.frame.size.width, kSizeOfClause)];
clauseLabel.text = [contract.textContract objectAtIndex:i];
thirdPlacement += clauseLabel.frame.size.height;
[thirdPage addSubview:clauseLabel];
[_scrollView addSubview:thirdPage];
}
} else {
articleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, placement, kTitleHeight, kTitleWidth)];
articleLabel.text = [NSString stringWithFormat:#"Article %i - %#",i, [contract.nameContract objectAtIndex:i]];
[_contractPageView addSubview:articleLabel];
placement += articleLabel.frame.size.height;
clauseLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, placement + kMarginLabel, _contractPageView.frame.size.width, kSizeOfClause)];
clauseLabel.text = [contract.textContract objectAtIndex:i];
placement += clauseLabel.frame.size.height;
[_contractPageView addSubview:clauseLabel];
}
}
I don't know if it's the only method, and imagine if I have many and many many many object in my array, I have to create more and more page :/ I don't know how to do it :(
Thanks for your help.

I did some similar code for a generic core data editor for one of our projects. So as I see it you got two options:
Use a collection view design the prototype view as your page and hook it up to an array controller that manages your text. The text could be organised as an array of NSAttributedString that you could bind to a NSTextView (one for each page)
If you need more flexibility, i.e. pages of different sizes, different layouts etc.., you could program your own container that keep as properties an array of "Pages" (you would need to define this class) and whenever this array is changed recomputes the sizes and constraints by, for example by adding up the heights of every pages and sizing your content view appropriately.
Hope it helps

Related

Displaying UILabel dynamcially from the NSMutableArray

I have a mutable array as below
labelArrays = [NSMutableArray arrayWithObjects:#"One”, #“Two”, #“Three", nil];
I want to display all the list array values as individual label. This is like displaying the UILabel dynamically using the array values. What i did is as below,
int count=20;
for(int i = 0; i < [labelArrays count]; i++){
label1 = [[UILabel alloc]init];
label1.text = [labelArrays componentsJoinedByString:#" "];
label1.textColor = [UIColor colorWithRed:0.643 green:0.643 blue:0.639 alpha:1];
label1.layer.borderWidth = 1.0;
label1.layer.cornerRadius = 8;
count+=20;
[self addSubview:label1];
}
Here I can only display all the array values inside the single label. How can i display the array values in multiple labels dynamically.
//viewcontroller
myView.label1.frame =CGRectMake(588,200,200,28);
your coding is fine , but you are not added the frame , try this
label1 = [[UILabel alloc] initWithFrame: CGRectMake(0,count,aslikeyourwidth,aslikeyourheight)];
Define a public method for view in which you are willing to add the UILabel, pass a CGRect to that method and then according to your design, with each iteration update the x coordinate or y coordinate. Follow the below steps:
// code in your viewcontroller
[myView designYourMultipleLabelsWithStartingFrame:CGRectMake(588,200,200,28)]; // pass the frame of the first label.
Now add the multiple UILabel in your view class, let's assume you have set labelArrays.
// code in view class
-(void) designYourMultipleLabelsWithStartingFrame:(CGRect) frame{
float xCoordinate = frame.origin.x;
float yCoordinate = frame.origin.y;
for(int counter = 0; counter < [labelArrays count]; counter++){
UILabel *label = [[UILabel alloc]initWithFrame:CGRectMake(xCoordinate,yCoordinate,labelWidth,labelHeight)];
label.text = [labelArrays objectAtIndex:counter];
[self addSubview:label];
// update the x coordinate or y coordinate according to your design. Let's say we need the labels in vertical order, so update the y Coordinate by labelHeight and gap between two labels.
yCoordinate = yCoordinate + labelHeight + gapBetweenTwoLabels.
}
}
Hope this will help.
Just a thought - would you not be better using a tableview to show the array?
It looks like you may need to just handle their position on the view as the code looks like it is adding the labels but it is placing them over each other.

How to set alignment for UIScrollView in ios

i am creating an application, where i am using UIScrollView for displaying the number of available colors of each product. I am getting number of available colors at run time, so i have used for loop and displaying on my scroll view. Its working fine, but the problem if i am getting only one available color then my output is coming as below screen :
But the output should be as below screen :
It seems like i need to do kind of center alignment for my UIScrollView, so every available color will suppose to display from center. here is my code :
UIScrollView *availableColorScrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 320, 80)];
availableColorScrollView.backgroundColor = [UIColor clearColor];
availableColorScrollView.showsHorizontalScrollIndicator = YES;
availableColorScrollView.showsVerticalScrollIndicator=NO;
availableColorScrollView.pagingEnabled = YES;
for (int i = 0; i < arrayAvlbleColors.count; i++) {
CGRect frame;
frame.origin.x = 23 * i;
frame.origin.y = 0;
frame.size = availableColorScrollView.frame.size;
UIView *subview = [[UIView alloc] initWithFrame:frame];
UILabel *label = [[UILabel alloc] init];
[label setFrame:CGRectMake(15, 14, 16, 16)];
[label.layer setCornerRadius:8];
NSString *strColorCode = [arrayAvlbleColors objectAtIndex:i];
strColorCode = [strColorCode substringFromIndex:1];
[label setBackgroundColor:[self colorWithHexString:strColorCode]];
[subview addSubview:label];
[availableColorScrollView addSubview:subview];
}
[self.view addSubview:availableColorScrollView];
availableColorScrollView.contentSize = CGSizeMake(24 * arrayAvlbleColors.count, availableColorScrollView.frame.size.height);
Please suggest me to achieve my output, Thanks!
this is the simple answer but working fine if u taken any Mainview or subview or else,
set the frame size/2 for height and width it automatically set it into center of the subview.
here i used static color , customize urself
just change your line this
NSString *strColorCode = [arrayAvlbleColors objectAtIndex:i];
strColorCode = [strColorCode substringFromIndex:1];
[label setBackgroundColor:[self colorWithHexString:strColorCode]];
[subview addSubview:label];
[availableColorScrollView addSubview:subview];
into
NSString *strColorCode = [arrayAvlbleColors objectAtIndex:i];
strColorCode = [strColorCode substringFromIndex:1];
[label setBackgroundColor:[self colorWithHexString:strColorCode]];
//use any one
//option no 1:
//[label setCenter:subview.center];
//option no 2:
[ label setCenter:CGPointMake(subview.frame.size.width / 2, subview.frame.size.height / 2)];
[subview addSubview:label];
[availableColorScrollView addSubview:subview];
final out put is
You can calculate your scrollview content to be placed in centre like this:
float contentWidth = 23 * arrayAvlbleColors.count;
Now you have content width to subtract from scrollView's width
float centerPadding = 0.0f;
float width = availableColorScrollView.frame.size.width;
centerPadding = width - contentWidth;
if(centerPadding < 0)
{
//content cannot be placed in center as content is bigger than width
centerPadding = 0.0f;
}
We have padding to center our content in scrollView, so use it in for loop like this :
frame.origin.x = (23 * i) + centerPadding;

Trying to create multiple views from one XIB file programmatically

I have a page enabled scroll view which I would like to fill with UIViews. I have tried creating a "XIB" file and loading from it in a for loop, but this only gives me one UIView in my scroll view.
int amount = garments.count;
[self.pageControl setNumberOfPages:amount];
for (int i = 0; i < amount; i++) {
DOVGarment *garment = garments[i];
CGRect frame;
frame.origin.x = self.scrollView.frame.size.width * i;
frame.size = self.scrollView.frame.size;
DOVGarmentDetailView *view = [DOVGarmentDetailView new];
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:#"detailView" owner:view options:nil];
view = [topLevelObjects objectAtIndex:0];
view.image.image = [UIImage imageNamed:garment.fileName];
view.labelID.text = garment.ID;
[self.scrollView addSubview:view];
}
self.scrollView.contentSize = CGSizeMake(self.scrollView.frame.size.width * amount, self.scrollView.frame.size.height);
Anyone have an idea why this doesn't work? Or if this is even possible?
You are creating multiple views but you are not changing the frame so they will all be placed directly on top of each other.
Update your code to offset each view relative to the one created before
Every view object you place in your xib file has only one instance of it. So every run cycle of your for loop you are accessing the same UIView instance.
Incase you need to fill your scrollView with view's you need to alloc and initialise again in every run cycle of the for loop thereby creating many instances of UIView.
This is how I did it which places UIViews horizontally in my scrollView -
for (int i = 0; i<numberOfViews; i++)
{
UIView*smallView = [[UIView alloc]initWithFrame:CGRectMake((i*scrollview.frame.size.width+heightOfYourView), 0, scrollview.frame.size.width, scrollview.bounds.size.height)];
[scrollview addSubview: smallView];
}
scrollview.contentSize = CGSizeMake(numberOfViews*scrollview.frame.size.width, scrollview.frame.size.height);

UIImageView animationDuration with fade effects

I'am searching for a (very) simple way to crossfade images within animationDuration time. I tried using NSTimer and some other stuff I found here, but no solution satisfy or helps me.
I create this stuff in a method (without comments) ...
- (UIImageView *)playSequence{
NSArray *structureArray = self.contenManager.ImageViewControllerSize;
NSMutableArray *structuresToPlay = [[NSMutableArray alloc] init];
// walk thru current collection
for(NSInteger i = 0; i < [self.currentCollection count]; i++)
{
NSInteger index = [[self.currentCollection objectAtIndex:i] integerValue];
[structuresToPlay addObject:[UIImage imageNamed:[structureArray objectAtIndex:index]]];
}
_playSequence = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 1024, 768)];
_playSequence.animationImages = structuresToPlay;
_playSequence.animationDuration = 5 * structuresToPlay.count;
[_playSequence startAnimating];
return _playSequence;
}
Here is the part where the method is called ...
- (void)justPlaySelection
{
UIView *justPlaySelection = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 1024, 768)];
// some background music code ...
// add play sequence
[justPlaySelection addSubview:self.playSequence];
// add close button
// { ... }
[self presentSemiView:justPlaySelection];
}
- (UIImageView *)playSequence
{
if (_playSequence == nil)
{
NSArray *structureArray = self.contenManager.ImageViewControllerSize;
NSMutableArray *structuresToPlay = [[NSMutableArray alloc] init];
// walk thru current collection
for(NSInteger i = 0; i < [self.currentCollection count]; i++)
{
NSInteger index = [[self.currentCollection objectAtIndex:i] integerValue];
[structuresToPlay addObject:[UIImage imageNamed:[structureArray objectAtIndex:index]]];
}
_playSequence = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 1024, 768)];
}
_playSequence.animationImages = structuresToPlay;
_playSequence.animationDuration = 5 * structuresToPlay.count;
[_playSequence startAnimating];
return _playSequence;
}
Here is another way to switch between to UIImageViews, it wouldn't be hard to add in the fade in and out.
- (void) performAnimationOfFrameAtIndex:(NSNumber*)indexNum
{
int index = indexNum.intValue;
BOOL completedSequece = index >= [self.frames count];
if (completedSequece) return;
UIImageView *imgIn;
UIImageView *imgOut;
if (index % 2 == 0) {
imgIn = self.animationImageViewOne;
imgOut = self.animationImageViewTwo;
}
else {
imgIn = self.animationImageViewTwo;
imgOut = self.animationImageViewOne;
}
imgIn.image = [self.frames objectAtIndex:index];
[self.view sendSubviewToBack:imgOut];
double speed = 0.1;
[self performSelector:#selector(performAnimationOfFrameAtIndex:) withObject:[NSNumber numberWithInt:index + 1] afterDelay:speed];
}
I created a project that does exactly what you describe (an effect like UIImageView animation sequence, but with a cross-fade between images.)
The project is on github: https://github.com/DuncanMC/Animate-Img
You need to have 2 image views.
The trick I use is to stack them on top of each other, and simply animate the top image view from alpha 1 -> 0, then switch images in the top image view and animate it from alpha 0 -> 1.0, so the new image is revealed.
The project demos a morph transition using only 5 frames. Each frame is marked with a number so you can see the frame changes. It has a switch which lets you turn cross-fading on or off.

How does one find a UIView... when there's no self.view?

So I've been fighting this one for a while as novice with iOS - I'm sure it's either a basic concept I'm missing, or a property I haven't run across yet that I need to reference.
Scenario: View controller creates a UIScrollView. UIView is created as a container for several UILabels (describing an event, venue and time). Method is called repeatedly to create these UILabels within the block. Creating these labels one by one works fine - simply adding each to the view - but when I move the code to a method and reuse it, abstracting such things as text size, indent, etc, I can't refer to the same parent view (because it's not a View Controller?), or search using viewWithTag (returns nothing) to find the parent.
Is this a simple fix, or is my basic structure flawed? Many thanks in advance for your time!
Header:
//
// ScheduleColumn.h
//
#import <Foundation/Foundation.h>
#interface ScheduleColumn : UIView {
}
- (void)makeTextBlock:(int)parentViewID label:(NSString*)label textSize:(int)textSize indent:(int)indent y:(int)y width:(int)width height:(int)height;
#end
Implementation:
//
// ScheduleColumn.m
//
#import "ScheduleColumn.h"
#implementation ScheduleColumn
// makeTextBlock: type, text, textSize, indent, build_y, width, height
// type: 0 = Title, 1 = Subtitle, 2 = Times
// text: Line content
// textSize: self-explanatory
// indent: indent from left side of parent
// build_y: # of units down from top of parent view to build
// width & height: self-explanatory
- (void)makeTextBlock:(int)parentViewID label:(NSString*)label textSize:(int)textSize indent:(int)indent y:(int)y width:(int)width height:(int)height {
double unixTime;
unixTime = [[NSDate date] timeIntervalSince1970];
NSLog(#"makeTextBlock called");
NSLog(#"parentViewID: %u", parentViewID);
NSLog(#"label: %#", label);
NSLog(#"textSize: %u", textSize);
NSLog(#"indent: %u", indent);
NSLog(#"y: %u", y);
NSLog(#"width: %u", width);
NSLog(#"height: %u", height);
NSLog(#"time: %u", unixTime);
UILabel *textView = [[UILabel alloc] initWithFrame:CGRectMake(indent, y, width, height)];
textView.backgroundColor = [UIColor clearColor];
textView.textColor = [UIColor whiteColor];
textView.lineBreakMode = UILineBreakModeWordWrap;
textView.numberOfLines = 0;
textView.tag = unixTime;
textView.font = [UIFont fontWithName:#"PetitaMedium" size: textSize];
textView.text = label;
CGSize constraintTextSize;
constraintTextSize.width = width;
constraintTextSize.height = MAXFLOAT;
CGSize theTextSize = [label sizeWithFont:[UIFont fontWithName:#"PetitaMedium" size: textSize] constrainedToSize:constraintTextSize lineBreakMode:UILineBreakModeWordWrap];
CGRect newTextFrame = textView.frame;
newTextFrame.size.height = theTextSize.height;
textView.frame = newTextFrame;
UIView *parentView = (UIView *)[self.view viewWithTag:parentViewID];
[parentView addSubview:textView];
[textView release];
NSLog(#"--- break ---");
}
.. and finally, the calls from the View Controller:
int build_y;
int subtitle_indent;
build_y = 30;
subtitle_indent = 20;
UIView *blockView = [[UIView alloc] initWithFrame: CGRectMake ( 0, build_y, 185, 50)];
blockView.tag = 100;
[FireworksContent addSubview:blockView];
// Add top line
UIView *lineView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, blockView.bounds.size.width, 0.5)];
lineView.backgroundColor = [UIColor whiteColor];
[blockView addSubview:lineView];
// Add Block Text
ScheduleColumn *blockText = [ScheduleColumn alloc];
[blockText makeTextBlock:blockView.tag label:#"Venue" textSize:18 indent:subtitle_indent y:build_y width:blockView.bounds.size.width height:20];
[blockText makeTextBlock:blockView.tag label:#"ShowTitle" textSize:12 indent:subtitle_indent y:build_y width:blockView.bounds.size.width height:20];
[blockText makeTextBlock:blockView.tag label:#"Showtime" textSize:36 indent:subtitle_indent y:build_y width:blockView.bounds.size.width height:20];
[lineView release];
[blockText release];
[blockView release];
... the viewWithTag line fails because "self" doesn't have a view... changing the class to a UIViewController lets it run, but still no joy.
A class method that returns a new view rather than an instance method that returns void would make more sense.
+(UIView *)makeTextBlock:(int)parentViewID label:(NSString*)label textSize:(int)textSize indent:(int)indent y:(int)y width:(int)width height:(int)height
Create the view as you want, and return that view at the end of the method.
Then you can create several of these views and hold a reference to them in your view controller if you want.
UIView *blockText1 = [ScheduleColumn makeTextBlock .....];
[self.view addSubview: blockText1];

Resources