Im relatively new to iOS development, and my constraints skill is at a basic level. I'm having a group of labels like so
However the Company Name and Address Line 2 are not always required so they are hidden in some cases, but when they are I don't know how to push the other labels up so that they fill the space created by the hidden labels.
Much appreciated if you guys can give a solution on this.
HI Joel why don't you try stack view inside table view cell and set
tableView.estimatedRowHeight = 44.0
tableView.rowHeight = UITableViewAutomaticDimension
and stackview and tableview will handle everything for you.
If you are using constraints then for instance the top label should have leading, trailing and top to superview.
Now this label will have its height depending on its font and text. If its text is empty then its height will be zero which you basically want.
Now next label is best to have leading and trailing to superview (or the label above it) and then vertical spacing to label before it. This means it will be just below the first one. And if first one has no text it will be on top.
Now do the same for all other labels one blow the other using vertical offset constraint.
Note for what you are doing it seems more appropriate to use an UITableView or maybe stack view. But if you want constraints then this is the procedure.
If things get complicated you can also drag constraints into your code and manipulate them manually.. For instance:
self.companyNameHeightConstraint.constant = myDataMode.companyName.isEmpty == true ? 0.0 : 50.0
EDIT: Since it is a bit unclear if fields are displayed depending on some external flags or if these strings are shown if they exist I am adding a minimum table view procedure with external flags:
#interface TableViewController () <UITableViewDelegate, UITableViewDataSource>
#property (nonatomic, strong) NSString *name;
#property (nonatomic, strong) NSString *lastName;
#property (nonatomic, strong) NSString *companyName;
#property (nonatomic, strong) NSString *address1;
#property (nonatomic, strong) NSString *address2;
#property (nonatomic, readonly) BOOL isAddress2Shown;
#property (nonatomic, readonly) BOOL isCompanyNameShown;
#end
#implementation TableViewController
- (NSArray *)generateDsiaplyableStrings {
NSMutableArray *array = [[NSMutableArray alloc] init];
if(self.name) [array addObject:self.name];
if(self.lastName) [array addObject:self.lastName];
if(self.companyName && self.isCompanyNameShown) [array addObject:self.companyName];
if(self.address1) [array addObject:self.address1];
if(self.address2 && self.isAddress2Shown) [array addObject:self.address2];
return array;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [self generateDsiaplyableStrings].count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSArray *items = [self generateDsiaplyableStrings];
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:nil];
cell.textLabel.text = items[indexPath.row];
return cell;
}
#end
Related
I am new to the autolayout thing, was trying to design the given screen for Compact Width|Regular Height via Storyboard. It seems Very simple cause there is none dynamic fields. I was following apple documentation, and setting constraints for each UIView. Its working fine if there would be no View(shown in purple color). But there is some requirement(have to hide the View based on star rating), thats why added this UIView containing dispatch details, complaint category, screenshot etc. e.g- For Invoice Number I set Top Space,Leading Space, Trailing Space and for the label(shown in green color) Top Space, Leading Space, Trailing Space so that height of invoice Number wouldn't be ambiguous rep, Repeated same for all Views. UIView(purple) constraints are Trailing Space to:SuperView, Bottom space to:SuperView, Bottom space to:Remarks Equals:15, Top Space Equals to:StarRating Equals:8. After that I have added constraints for the fields inside the purple View same as the other Views like Invoice Number. But not getting the desired result for different screen size. Any help would be much appreciated.
Use a UITableView to lay out this screen. You can use a static content table view, laid out entirely in the storyboard. Make the purple part its own section. In your UITableViewController subclass, you don't need to implement most data source / delegate methods (because you're using static content). Just override tableView:numberOfRowsInSection: to return 0 for the optional section if you don't want to show it, and use -[UITableView reloadSections:withRowAnimation;] to update the table view. Here's my test code:
#import "ViewController.h"
#interface ViewController ()
#property (strong, nonatomic) IBOutlet UITableViewCell *topMarginCell;
#property (strong, nonatomic) IBOutlet UIButton *star1;
#property (strong, nonatomic) IBOutlet UIButton *star2;
#property (strong, nonatomic) IBOutlet UIButton *star3;
#property (strong, nonatomic) IBOutlet UIButton *star4;
#property (strong, nonatomic) IBOutlet UIButton *star5;
#property (strong, nonatomic) NSArray<UIButton *> *starButtons;
#property (nonatomic) NSInteger rating;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.starButtons = #[ self.star1, self.star2, self.star3, self.star4, self.star5 ];
self.tableView.backgroundColor = self.topMarginCell.backgroundColor;
}
- (void)reloadDispatchSection {
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:1] withRowAnimation:UITableViewRowAnimationNone];
}
- (IBAction)starButtonWasTapped:(UIButton *)button {
NSInteger buttonIndex = [self.starButtons indexOfObject:button];
if (buttonIndex == NSNotFound) { return; }
self.rating = buttonIndex + 1;
[self.starButtons enumerateObjectsUsingBlock:^(UIButton * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
[obj setTitle:(idx <= buttonIndex ? #"★" : #"☆") forState:UIControlStateNormal];
}];
[self reloadDispatchSection];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (section == 1 && (self.rating == 0 || self.rating >= 4)) {
return 0;
} else {
return [super tableView:tableView numberOfRowsInSection:section];
}
}
#end
Result:
I have create a UITableView With a Customized TableViewCell,
#import <UIKit/UIKit.h>
#import "Equipment.h"
#import "MCStepperViewController.h"
#class EquipmentCell;
#protocol EquipmentCellDelegate <NSObject>
- (void)EquipmentCell:(EquipmentCell *)cell didChangeStepperValue:(int)stepperValue indexPath:(NSIndexPath*)indexPath;
#end
#interface EquipmentCell : UITableViewCell <MCStepperDelegate>
#property (nonatomic, strong) UIImageView* eqiupmentImage;
#property (nonatomic, strong) UILabel* Title;
#property (nonatomic, strong) UILabel* details;
#property (nonatomic, strong) UILabel* prices;
#property (nonatomic, strong) MCStepperViewController* stepper;
#property (nonatomic, weak) id <EquipmentCellDelegate> delegate;
#property (nonatomic, assign) NSIndexPath* indexPath;
-(void)setContent:(Equipment*)equip;
-(void)setAmount:(int)eq_amount;
#end
The customized cell owns a stepperview controller, and I add the stepperViewController.view to the content view of the cell.
The stepperViewController has a textField and two buttons as strong properties.
The confusing part is, when I scroll the table view, the table cells are ready for reuse process.
The property views of the cell works well, but the view of stepperViewController, which is the subview of the contentView, is becoming duplicate.
Although I have set cell.stepper.view.textField to the right value, but it still doesn't work.
Is there any good solution? I don't think disable reusability is a good attempt.
Thank you very much!
EDIT:
The cellForRowAtIndexPath method:
else if (tableView == _subTable)
{
EquipmentCell *cell = [tableView dequeueReusableCellWithIdentifier:#"detailTable" forIndexPath:indexPath ];
if(!cell)
{
cell = [[EquipmentCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"detailTable"];
}
cell.indexPath = indexPath;
Equipment* item = [[[_goodsList objectAtIndex:indexPath.section] objectForKey:#"data"] objectAtIndex:indexPath.row];
cell.delegate = self;
[cell setContent:item];
if (countDic) {
if ([countDic objectForKey:[NSString stringWithFormat:#"%lu", (indexPath.section*100)+(indexPath.row)]]) {
[cell setAmount:(int)[[countDic objectForKey:[NSString stringWithFormat:#"%lu", (indexPath.section*100)+(indexPath.row)]] integerValue]];
}
else
{
[cell setAmount:0];
}
}
return cell;
}
I tried to assign the textfield.text in cell class, but is doesn't work.
- (void)setAmount:(int)eq_amount
{
_stepper.amount = eq_amount;
_stepper.amountText.text = [NSString stringWithFormat:#"%d", eq_amount];
if (eq_amount == self.stepper.min) {
_stepper.minusButton.hidden = YES;
_stepper.amountText.hidden = YES;
}
}
Check where you add your stepperViewController.view to the content view of the cell.
You want to support view recycling for your tableview cell. So as soon as your cell1 is not visible anymore and data 2 needs a cell (cellForRowAtIndexPath gets called), this reused cell will be cell 1. Instead of data1 cell1 now displays data2. If you add the stepperViewController.view in the cellForRowAtIndexPath function, there will be the view added for data1. now if cell1 isn't visible anymore and should be used for data2 you should check if you add the stepperViewController.view again.
Instead of simply adding the view, you could check if there is already a stepperViewController.view in you cell's subviews and remove or reuse it.
I can't figure out why my UILabels in my custom TableViewCell are nil in CellForRowAtIndexPath. I used the same method as I did for a previous TableView in another view Controller. Here's what I already checked:
Cell Style is Custom
Cell has a unique identifier "profilecell"
Cell's class is of the desired custom type
data source and delegated are connected to the View Controller
Each of the 5 buttons/labels is connected to its respective property in the custom class
Dynamic Prototypes is selected
I am able to change the background color of the cell and populate the textLabel.text -- it's just my UILabels that aren't filled.
Here is CellForRowAtIndexPath. It's the exact same method as my other TableVC, which works. Also, I don't need to actually reuse this custom cell, should I be using a different method?
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifierProfile = #"profilecell";
ProfileSection0TableViewCell *profilecell = [tableView dequeueReusableCellWithIdentifier:CellIdentifierProfile];
if (profilecell == nil) {
profilecell = [[ProfileSection0TableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifierProfile];
}
if (indexPath.section == 0) {
[profilecell setBackgroundColor:[UIColor colorWithRed:54/255.0f green:61/255.0f blue:147/255.0f alpha:1.0f]];
if (indexPath.row == 0) {
profilecell.labelName.text = _userProfile.fullName;
NSString *test = [NSString stringWithFormat:#"%#", profilecell.labelName.text];
NSLog(#"text %#", test); //shows that label is (null)
profilecell.labelName.textColor = [UIColor whiteColor];
//profilecell.labelSpecialty.text = _userProfile.specialty;
//profilecell.labelSpecialty.textColor = [UIColor whiteColor];
}
}
return profilecell;
}
Thanks so much in advance.
.h file:
#interface ProfileSection0TableViewCell : UITableViewCell
#property (nonatomic, weak) IBOutlet UILabel* labelName;
#property (nonatomic, weak) IBOutlet UILabel* labelSpecialty;
#property (nonatomic, weak ) IBOutlet UILabel* labelCurrentRole;
#property (nonatomic, weak) IBOutlet UILabel* labelPosition;
#property (nonatomic, weak) IBOutlet UIButton* facePicture;
#end
.m file:
#implementation ProfileSection0TableViewCell
#synthesize labelName, labelSpecialty, labelPosition, facePicture, labelCurrentRole;
#end
EDIT:
It looks like ProfileSection0TableViewCell is allocated properly, but the member vars inside the cell are nil. And in outlets, here are the connections:
facePicture - Button
labelName- UILabel "person"
labelPosition - UILabel "position"
List item - UILabel "specialty"
labelCurrentRole - UILabel titled school
Either _userProfile.fullName is nil or profilecell.labelName is nil
If you made the cell in interface builder, did you hook up the outlet to labelName correctly?
This has been driving me crazy and not really sure whats going on. I have a UITableView and I'm populating the content of the UITableViewCells like so
- (UITableViewCell *)tableView:(UITableView *)a_tableView cellForRowAtIndexPath:(NSIndexPath *)a_indexPath
{
NewsItemCell * cell = (NewsItemCell *)[a_tableView dequeueReusableCellWithIdentifier:#"NewsItemCell"];
if(cell == nil)
{
cell = [[[NewsItemCell alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 0)] autorelease];
}
if(!m_isLoading)
{
NewsFeed * feed = [NewsFeed sharedNewsFeed];
NewsEntry * entry = [[feed feedEntries] objectAtIndex:[a_indexPath row]];
[cell setNewsEntry:entry];
}
else
{
if([a_indexPath row] < [m_visibleCells count])
{
return [m_visibleCells objectAtIndex:[a_indexPath row]];
}
}
return cell;
}
The line that contains [cell setNewsEntry:entry] takes the NewsEntry object and sets the cells textLabel.text to a string contained in entry. This works fine in iOS 6 but in iOS 7 the labels aren't showing the text.
- (void)setNewsEntry:(NewsEntry *)a_newsEntry
{
self.textLabel.text = a_newsEntry.title;
self.detailTextLabel.text = a_newsEntry.summary;
self.imageView.image = [self getIconForFeedType:[a_newsEntry feedType]];
}
I've done some poking around and in setNewsEntry and if I set self.textLabel.text to a string literal e.g. #"hello" then it works fine. I've also don't a lot of NSLogs inside of setNewsEntry to make sure that a_newsEntry actually has a valid title and summary properties, it does but for some reason the textLabel doesn't seem to be retaining them.
At first I was thinking that this was some memory issue with NewsEntry object but it doesn't make sense to me why this would be the case in iOS 7 and not iOS 6. Shouldn't the textLabel.text property retain the NSString that it gets set to?
If anyone has any ideas as to why this is happening please let me know and I can provide answers to any questions you have.
Thank you.
EDIT: NewsEntry Interface
#interface NewsEntry : NSObject
{
NSString * m_title;
NSString * m_summary;
NSString * m_published;
NSString * m_updated;
NSString * m_link;
int m_feedType;
}
#property (nonatomic, readonly) NSString * title;
#property (nonatomic, readonly) NSString * summary;
#property (nonatomic, readonly) NSString * published;
#property (nonatomic, readonly) NSString * updated;
#property (nonatomic, readonly) NSString * link;
#property (nonatomic, readonly) int feedType;
- (NSDictionary *)properties;
- (NewsEntry *)initWithProperties:(NSDictionary *)a_properties;
- (NSComparisonResult)compareEntry:(NewsEntry *)entry;
#end
Where you able to resolve it?
I have a similar issue where the detailTextLabel does not get updated on iOS7 whereas it works fine on iOS6. In my case, if I present another view on top and than go back to the tableview, I see the label updated already.
I have a UITableView that covers the whole screen, with a lot of cells.
On the view that holds the UITableView I want to display an UIImage at the top right corner.
This UIImage (or UIImageView) is fixed on the UIView - it is not part of the tableView (in the z order it is over the tableView).
It is possible to let the UITableView float around this image, even when scrolling?
Below an image of my desired structure. Imagine that they are more cells even under der image. The cells (or UILabels) that are covered by the image should be truncated (or do a line break).
Is this possible? Currently I have no idea how to do this.
One option is to adjust the trailing space constraint of a label on your custom table view cell based on tableview's scrollview delegate method. You need to check if your cell is in the area of your imageView and then adjust the constraint. You also need to adjust the constraint in your cellForRowAtIndexPath for the initial layout.
Storyboard
ViewController.m
#import "ViewController.h"
#import "MyCell.h"
#interface ViewController () <UITableViewDataSource, UITableViewDelegate>
#property (weak, nonatomic) IBOutlet UITableView *tableView;
#property (weak, nonatomic) IBOutlet UIImageView *imageView;
#end
#implementation ViewController
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 20;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
MyCell *cell = (MyCell *)[tableView dequeueReusableCellWithIdentifier:#"MyCell" forIndexPath:indexPath];
// Configure the cell...
cell.myLabel.text = #"Lorem ipsum dolor sit amet, etc.";
if (indexPath.row >=3 && indexPath.row <= 5) {
cell.trailingSpaceConstraint.constant = 168;
}
return cell;
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
NSUInteger count = 0;
for (MyCell *cell in [self.tableView visibleCells]) {
if (count >= 3 && count <= 6) {
cell.trailingSpaceConstraint.constant = 168.0;
} else {
cell.trailingSpaceConstraint.constant = 20.0;
}
count++;
}
}
#end
MyCell.h
#import <UIKit/UIKit.h>
#interface MyCell : UITableViewCell
#property (weak, nonatomic) IBOutlet UILabel *myLabel;
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *trailingSpaceConstraint;
#end
Output
In this sample you need to use custom ViewController, add tableView and imageView, hook tableView's delegate and datasource and outlet. You also need to add custom subclass of UITableViewCell (MyCell) and hook outlets of the cell's label and the trailing space constraint.
The check at scrollViewDidScroll for rows is quick and dirty. Probably a better solution might be to check which cells are touching the imageView rect in indexPathsForRowsInRect.