Constraint spacing UILabel in For Loop - objective-c

I have a small problem and hope someone can help me solve it ... I searched the net and made some attempts, but I could not solve my problem.
I created a FOR loop that plays a specified number of labels obtained through an array, as you can see from the code that I posted below ...
Now my problem is how to insert constraint programmatically. I need that between a label and the other (horizontal) there is a specific space.
Example:
if I label 4 I would like these would take around the horizontal space is in the iPhone display SE both iPhone 7 Plus ..
So my question is how do I set a specific space between each label and pass in the FOR loop?
CGFloat padding = 0;
NSMutableArray *array = [[NSMutableArray alloc] initWithObjects:#"INFO UNIVERSITARIE",#"INFO GENERALI", #"TROVA SEDI", nil];
for (NSInteger i = 0; i < array.count; i ++) {
NSString *valueText = [array objectAtIndex:i];
UILabel *label = [[UILabel alloc] init];
label.frame = CGRectMake(padding *i, 0, 50, 30);
label.text = [NSString stringWithFormat:#"%#", valueText];
label.textColor = [UIColor whiteColor];
label.font = [UIFont fontWithName:#"HelveticaNeue" size:12];
[label sizeToFit];
[_baseView addSubview:label];
padding = label.frame.size.width;
label.translatesAutoresizingMaskIntoConstraints = NO;
}

Researching this, I remembered that the UIStackView is our new friend, designed specifically for this problem.
NSArray *array = #[ #"INFO UNIVERSITARIE",#"INFO GENERALI", #"TROVA SEDI" ];
NSMutableArray *labels = [NSMutableArray array];
for (NSString *string in array) {
UILabel *label = [[UILabel alloc] init];
label.text = string;
[labels addObject:label];
}
UIStackView *stackView = [[UIStackView alloc] initWithArrangedSubviews:labels];
stackView.axis = UILayoutConstraintAxisHorizontal;
stackView.spacing = 12.0;
stackView.frame = self.view.bounds;
[self.view addSubview:stackView];
Just learned about this myself, but I tried this code and it works nicely.

Related

CAGradientLayer in UILabel in UIStackView

This is a custom UIView subclass for a tableHeaderView.
I'm currently trying to put a gradient to an UILabel that's in a vertical UIStackView. It works really well without gradient even in RTL languages (gets moved to the right), but as soon as I add the gradient, the label that's the maskView of the gradientView gets completely out of its place. Here the code for better comprehension:
- (instancetype)initWithTitle:(NSString *)title subtitle:(NSString *)subtitle colors:(NSArray<UIColor *> *)colors {
if (self = [super initWithFrame:CGRectMake(0, 0, self.superview.frame.size.width, 200)]) {
// Labels
UILabel *titleLabel = [[UILabel alloc] init];
titleLabel.text = title;
titleLabel.font = [UIFont boldSystemFontOfSize:42.f];
titleLabel.lineBreakMode = NSLineBreakByWordWrapping;
[titleLabel sizeToFit];
UILabel *subtitleLabel = [[UILabel alloc] init];
subtitleLabel.text = subtitle;
subtitleLabel.font = [UIFont systemFontOfSize:24.f];
subtitleLabel.lineBreakMode = NSLineBreakByWordWrapping;
[subtitleLabel sizeToFit];
// Create gradient
CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.frame = titleLabel.bounds;
NSMutableArray *cgcolors = [NSMutableArray new];
for (int i = 0; i < colors.count; i++) {
cgcolors[i] = (id)colors[i].CGColor;
}
gradient.colors = cgcolors;
gradient.locations = #[#0, #1];
UIView *gradientView = [[UIView alloc] initWithFrame:titleLabel.bounds];
[gradientView.layer addSublayer:gradient];
[gradientView addSubview:titleLabel];
gradientView.maskView = titleLabel;
// Stack
UIStackView *stackView = [[UIStackView alloc] initWithArrangedSubviews:#[gradientView, subtitleLabel]];
stackView.alignment = UIStackViewAlignmentLeading;
stackView.axis = UILayoutConstraintAxisVertical;
stackView.distribution = UIStackViewDistributionEqualCentering;
stackView.spacing = 0;
stackView.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:stackView];
[NSLayoutConstraint activateConstraints:#[
[stackView.leadingAnchor constraintEqualToAnchor:self.leadingAnchor constant:25],
[stackView.centerYAnchor constraintEqualToAnchor:self.centerYAnchor],
[stackView.heightAnchor constraintEqualToConstant:titleLabel.frame.size.height + subtitleLabel.frame.size.height]
]];
}
return self;
}
With LTR languages, it looks good, but with RTL languages, the frame of the gradientView that contains the titleLabel becomes {[width of subtitleLabel], 0, 0, 0} for no reason. The cause is the gradient view, because without it it works fine, but I can't find why.
I tried titleLabel.layer.mask, placing the gradient block at the end, but nothing worked.
EDIT :
Working code:
- (instancetype)initWithTitle:(NSString *)title subtitle:(NSString *)subtitle colors:(NSArray<__kindof UIColor *> *)colors {
if (self = [super initWithFrame:CGRectMake(0, 0, self.superview.frame.size.width, 200)]) {
// Labels
UILabel *titleLabel = [[UILabel alloc] init];
titleLabel.text = title;
titleLabel.font = [UIFont boldSystemFontOfSize:42.f];
titleLabel.lineBreakMode = NSLineBreakByWordWrapping;
[titleLabel sizeToFit];
UILabel *subtitleLabel = [[UILabel alloc] init];
subtitleLabel.text = subtitle;
subtitleLabel.font = [UIFont systemFontOfSize:24.f];
subtitleLabel.lineBreakMode = NSLineBreakByWordWrapping;
[subtitleLabel sizeToFit];
// Create gradient
CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.frame = titleLabel.bounds;
NSMutableArray *cgcolors = [NSMutableArray new];
for (int i = 0; i < colors.count; i++) {
cgcolors[i] = (id)colors[i].CGColor;
}
gradient.colors = cgcolors;
gradient.locations = #[#0, #1];
UIView *gradientView = [[UIView alloc] init];
[gradientView.layer addSublayer:gradient];
gradientView.maskView = titleLabel;
// Stack
UIStackView *stackView = [[UIStackView alloc] initWithArrangedSubviews:#[gradientView, subtitleLabel]];
stackView.alignment = UIStackViewAlignmentLeading;
stackView.axis = UILayoutConstraintAxisVertical;
stackView.distribution = UIStackViewDistributionEqualCentering;
stackView.spacing = 0;
stackView.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:stackView];
[NSLayoutConstraint activateConstraints:#[
[stackView.leadingAnchor constraintEqualToAnchor:self.leadingAnchor constant:25],
[stackView.centerYAnchor constraintEqualToAnchor:self.centerYAnchor],
[stackView.heightAnchor constraintEqualToConstant:titleLabel.frame.size.height + subtitleLabel.frame.size.height],
[gradientView.widthAnchor constraintEqualToConstant:titleLabel.frame.size.width],
[gradientView.heightAnchor constraintEqualToConstant:titleLabel.frame.size.height]
]];
}
return self;
}
The problem is that your gradientView has no intrinsic content size.
Try changing your constraints to this:
[NSLayoutConstraint activateConstraints:#[
[stackView.leadingAnchor constraintEqualToAnchor:self.leadingAnchor constant:25],
[stackView.centerYAnchor constraintEqualToAnchor:self.centerYAnchor],
// shouldn't need to set a height anchor on the stackView
//[stackView.heightAnchor constraintEqualToConstant:titleLabel.frame.size.height + subtitleLabel.frame.size.height],
[gradientView.widthAnchor constraintEqualToConstant:titleLabel.frame.size.width],
[gradientView.heightAnchor constraintEqualToConstant:titleLabel.frame.size.height],
]];

Objective-C loop needed

I need some help on automating this with a loop (as I have 500+ buttons). The code is bellow. Thank you.
MyButton *button1 = [[MyButton alloc] init];
button1.name = #"One";
button1.controller = self;
button1.image = [NSImage imageNamed:button1.name];
_buttonArray = [[NSMutableArray alloc] init];
[arrayController addObject:button1];
MyButton *button2 = [[MyButton alloc] init];
button2.name = #"Two";
button2.controller = self;
button2.image = [NSImage imageNamed:button2.name];
_buttonArray = [[NSMutableArray alloc] init];
[arrayController addObject:button2];
Better to name the buttons with a pattern like: "Button125", etc.
Example code, not tested:
for (int i=1; i<= 500; i++) {
[MyClass createButtonNumber:i]; // Where MyClass is the class name this code is in.
}
+ (MyButton *)createButtonNumber:(int)number {
MyButton *button = [MyButton new];
button.name = [NSString stringWithFormat:#"Button%03i", number];
button.controller = self;
button.image = [NSImage imageNamed: button.name];
[arrayController addObject:button];
return button; // Just incase it is needed.
}
Note: The following code repeated for each button makes no sense and has been left out of the example code.
_buttonArray = [[NSMutableArray alloc] init];
for (NSInteger i = 1; i <= 500; ++i) {
MyButton *button = [[MyButton alloc] init];
button.name = ...; // Needs a method to convert from i to corresponding English words
button.controller = self;
button.image = [NSImage imageNamed:button.name];
[arrayController addObject:button];
}
You need an algorithm to convert from i to English words (e.g 1 to "One", 2 to "Two" etc). This can be done with an NSNumberFormatter with numberStyle set to NSNumberFormatterSpellOutStyle, see this for more info.
You still needs 500+ images named according to the buttons' names.
It's just a guess, but UITableView and UITableViewCell might be what you need. I can think of no sane reason to use 500 UIButtons.

Change image sizes in NSArray Function - XCode

I'm trying to create myself a basic iOS app with nothing but HTML knowledge.
I added a code I found for an in-App animation but the animation's dimensions and placement are wrong. Here is the code:
NSArray *imageNames = #[#"fold1.png", #"fold2.png", #"fold3.png", #"fold4.png",
#"fold5.png", #"fold6.png", #"fold7.png", #"fold8.png",
#"fold9.png", #"fold10.png", #"fold11.png", #"fold12.png",
#"foldclear.png"];
NSMutableArray *images = [[NSMutableArray alloc] init];
for (int i = 0; i < imageNames.count; i++) {
[images addObject:[UIImage imageNamed:[imageNames objectAtIndex:i]]];
}
// Normal Animation
UIImageView *animationImageView = [[UIImageView alloc] initWithFrame:CGRectMake(60, 95, 86, 193)];
animationImageView.animationImages = images;
animationImageView.animationDuration = 0.5;
animationImageView.animationRepeatCount = 1;
[self.view addSubview:animationImageView];
[animationImageView startAnimating];
}
Can anybody help me?
All you have to do is to change the coordinates in CGRectMake().
CGRectMake(x, y, width, height)

Adding text to a UIScrollView

I'm creating a tape measure in a UIScrollView, it has horizontal lines from an image and I want to add the numbers programatically.
(Note the blue was to help getting the layout right)
I tried this:
for (NSInteger i = 0; i < self.maxUnits.integerValue; i++) {
static CGFloat labelWidth = 40;
CGFloat x = (halfScreen - (labelWidth/2)) + (i * self.widthOfOneFraction.floatValue * self.numberOfFractions.floatValue);
CGRect frame = CGRectMake(x-1, 41, labelWidth, 70);
UILabel *label = [[UILabel alloc] initWithFrame:frame];
label.text = [NSString stringWithFormat:#"%d", i];
label.textAlignment = NSTextAlignmentCenter;
label.textColor = [UIColor tlbBlueColor];
label.font = [UIFont fontWithName:#"HelveticaNeue-Light" size:20.0];
[self addSubview:label];
}
Which works, but uses about 40Mb of RAM. I thought that adding the text in a CATextLayer might be more efficient, but I can't get it working:
for (NSInteger i = 0; i < self.maxUnits.integerValue; i++) {
CATextLayer *label = [[CATextLayer alloc] init];
[label setFont:#"HelveticaNeue-Light"];
[label setFontSize:20];
[label setFrame:frame];
[label setString:[NSString stringWithFormat:#"%d", i]];
[label setAlignmentMode:kCAAlignmentCenter];
[label setForegroundColor:[[UIColor whiteColor] CGColor]];
[self.layer addSublayer:label];
}
The numbers don't appear on the screen.
So my question is can I either make the first way more efficient, and if not is the second way more efficient when it works and if so how do I get it working?
I changed the way the loading worked so as to only load items on display and to load a few more as you scroll.
This seems to have totally fixed the high memory issue and even if you scroll to both ends it doesn't use the same high amount of memory. I guess there was a leak in how my original code worked.

How to place "x" number of objects on the view

If the user provides a number, such as 8, how exactly would I place eight labels onto the view?
For example:
int userGivenNumber = textfield.text;
for (int labelNumber=1; i<=userGivenNumber; i++) {
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(50, *previousLabel*.frame.origin.y + 20)];
}
How would you get the location of previousLabel? Additionally, if the user wanted to edit a label, such as label 3, how would I edit this label?
The problem is that I don't know how many labels they want to place while coding, and I have no way of keeping track of the labels, since they are called label. Do you have any ideas? Do you understand what I am asking? Thanks for your help.
- (void)addLabels {
CGFloat offset = 0;
// labels is NSMutableArray property
labels = [[NSMutableArray alloc] initWithCapacity:8];
for (int i = 0; i < 8; i++) {
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, offset, 320, 20)];
[self.view addSubview:label];
[labels addObject:label];
offset += label.frame.size.height;
}
}
Edit: Like jerrylroberts said, to access the label array from elsewhere in your code, you should declare it as a property.
So you could add the views like so:
int userGivenNumber = [textfield.text intValue];
for (int labelNumber=1; i<=userGivenNumber; i++) {
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(50, *previousLabel*.frame.origin.y + 20)];
[self.view addSubview:label];
[label release];
}
If you wanted to keep track of them you could just create a mutable array as a property and then add each label to the array before you add to the subview;
Interface
#property (nonatomic, retain) NSMutableArray *addedLabels;
Implementation
#synthesize addedLabels=_addedLabels;
- (void)viewDidLoad
[super viewDidLoad];
// Create your array to hold labels
NSMutableArray *addedLabels = [[NSMutableArray alloc] initWithCapacity:0];
self.addedLabels = addedLabels;
[addedLabels release];
// NOW put your code
int userGivenNumber = [textfield.text intValue];
for (int labelNumber=1; i<=userGivenNumber; i++) {
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(50, *previousLabel*.frame.origin.y + 20)];
[self.view addSubview:label];
[addedLabels addObject:label];
[label release];
}
}
Now you can easily access any label you have added if you have the index. Hope this helps
Just keep a reference to the location (CGPoint) outside the loop, but update it inside the loop to the current label's location. When you assign the next frame do your operation depending on width and size of the label, and don't forget to update the reference of CGPoint.