iOS - UIViewController as Popup with dynamic height - ios

I have a Popup-View which contains two labels, a tableview and button. I created a ViewController as in Display UIViewController as Popup in iPhone described.
My special requirement is now, that the tableview is not necessary in all cases, so I tried to hide it and expected a reduced height of the Popup-View. But I always get the same height. I also tried to use a UIStackView, but the height of the view wasn't changed in case of hiding the tableview.
I also need to have the view in center of the display in both cases of height.
enter image description here
#interface AuthorizationMessageViewController ()
#property (weak, nonatomic) IBOutlet UIView *messageView;
#property (weak, nonatomic) IBOutlet UILabel *titelLabel;
#property (weak, nonatomic) IBOutlet UILabel *detailsLabel;
#property (weak, nonatomic) IBOutlet UITableView *tableView;
#property (weak, nonatomic) IBOutlet UIButton *okButton;
- (IBAction)okButtonTouchUp:(id)sender;
#end
#implementation AuthorizationMessageViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.messageView.layer.cornerRadius = 5;
self.messageView.layer.masksToBounds = YES;
self.messageView.backgroundColor = COLOR_BACKGROUND_WHITE;
self.messageView.autoresizingMask = UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin;
self.tableView.delegate = self;
self.tableView.dataSource = self;
self.tableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero];
self.tableView.separatorColor = [UIColor clearColor];
self.tableView.backgroundColor = COLOR_BACKGROUND_WHITE;
self.titelLabel.text = WHLocalizedString(#"EventHeaderAuthorization", nil);
[self setupView];
}
- (void)setupView
{
NSString *ns_messageText;
ns_messageText = #"test";
if (YES)
{
ns_messageText = #"Hide"
[self.tableView setHidden:YES];
}
else
{
ns_messageText = #"No Hide"
[self.tableView setHidden:NO];
}
self.detailsLabel.text = ns_messageText;
self.detailsLabel.textColor = COLOR_TEXT_GREY_KEY;
self.detailsLabel.numberOfLines = 0;
[self.detailsLabel sizeToFit];
}
#pragma mark - Table view data source
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 7;
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 21;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:#"testCell"];
UILabel *weekDayLabel = (UILabel *)[cell viewWithTag:10];
weekDayLabel.text = #"weekday";
weekDayLabel.textColor = COLOR_TEXT_GREY_KEY;
weekDayLabel.font = FONT_LIGHT_SIZE_15;
UILabel *testLabel = (UILabel *)[cell viewWithTag:11];
testLabel = #"testLabel"
testLabel = COLOR_TEXT_GREY_KEY;
testLabel = FONT_LIGHT_SIZE_15;
[cell setSelectionStyle:UITableViewCellSelectionStyleNone];
return cell;
}
I hope that someone has a solution or an idea for that.

Imagine the following UI
a centered UIView that contains a UIButton and UITableView , you need to hook height constraint of the table and do this inside the popup if you want to hide it
#IBOutlet weak var heightTblCon:NSLayoutConstraint!
//
self.heightTblCon.constant = show ? 300 : 0
self.view.layoutIfNeeded()
BTW i changed color of background view for clarification purposes that should be transparent for modals

Try calling sizeToFit() on Popup-View after hiding the tableView:
popupView.sizeToFit()

Try to Adjust the Height of your view using the NSLayoutConstraint. Create an outlet for the same and manage height programatically. For example:
myConstraintOutlet.constant = 10
myTableView.layoutIfNeeded()
Here is the link for further assistant.
Regarding your second query to have the pop up always in centre you can either do it using Autolayout or programatically define the centre of your pop up.
myView.center = CGPoint(x: superView.center.x, y: superView.center.y)
Hope that helps.

Related

Reuse custom TableViewCell

I have a viewController that contains a programmatically created tableView.
#property (strong, nonatomic) UITableView *numbersTableView;
//...
self.numbersTableView = [[UITableView alloc] initWithFrame:CGRectMake(0, -100, self.view.frame.size.width-20, 50)];
self.numbersTableView.delegate = self;
self.numbersTableView.dataSource = self;
self.numbersTableView.tag = 1;
self.numbersTableView.layer.cornerRadius = 5;
[self.numbersTableView setBounces:false];
[self.numbersTableView setHidden:true];
[self.numbersTableView registerClass:[AZGCountryCell class] forCellReuseIdentifier:#"NumbersTableViewCell"];
[self.view addSubview:self.numbersTableView];
For the prototype cell I want to use a prototype that I created somewhere else in another viewController and I designed It in storyboard.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
if (tableView.tag == 1) { //tableView == self.numbersTableView
NSString *cellID = #"NumbersTableViewCell";
AZGCountryCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];
if (cell == nil) {
cell = [[AZGCountryCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellID];
}
cell.countryName.text = self.numbersArray[indexPath.row].number;
cell.flagImageView.image = [UIImage imageNamed:self.numbersArray[indexPath.row].country];
return cell;
}
}
And my custom cell contains one UIImageView and one UILabel but when I run the code the cells are empty and I can see the UIImageView and UILabel are nil!
#import <UIKit/UIKit.h>
#interface AZGCountryCell : UITableViewCell
#property (strong, nonatomic) IBOutlet UIImageView *flagImageView;
#property (strong, nonatomic) IBOutlet UILabel *countryName;
#property (strong, nonatomic) IBOutlet UILabel *countryCode;
#end
#import "AZGCountryCell.h"
#implementation AZGCountryCell
#synthesize flagImageView;
#synthesize countryName;
- (void)awakeFromNib {
[super awakeFromNib];
self.flagImageView.layer.cornerRadius = self.flagImageView.frame.size.width/2;
self.flagImageView.layer.masksToBounds = YES;
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
}
#end
Then what should I do to have properly filled cells in my numbersTableView?
Unfortunately you can't reuse a table cell you've defined in a table view in a storyboard in your separate class.
In order for you to be able to share your cell between the different views, you'll have to extract the view to a separate nib and register that nib go be used on both tables.
Alternatively you can implement the cell's UI in code in the table view cell subclass.
You getting empty cell because custom tableview cell not registered, use
[self.numbersTableView registerNib:[UINib nibWithNibName:#"AZGCountryCell" bundle:nil] forCellReuseIdentifier:#"NumbersTableViewCell"];
instead of
[self.numbersTableView registerClass:[AZGCountryCell class] forCellReuseIdentifier:#"NumbersTableViewCell"];

How I can make a custom UITableViewCell with 2 label so if the text of a label is empty the other fills the space?

Example:
Text in textLabel and descriptionLabel isn't empty
Text descriptionLabel is empty
It's possible con Autolayout or it's necessary to override some method.
OK, answer version 1.1. This time we do this:
Add your top titleLabel: Set a vertical constraint in auto layout to the superview. Go to your size inspector and change the constant to -15 (which will slide it up). Set another constraint to the top of the view to whatever you want for padding. Set the priority on that constraint to 900.
Now control drag from the vertical constraint of tltleLabel to the interface of your .m file to create an outlet for the constraint.
Add your bottom subtitleLabel: Set a vertical constraint to the superview with a multiplier of 1.5 (or whatever you need to make it look OK). Don't add any constraints that show a relationship with the top label.
In your code, if you have a subtitle, then just fill your labels and you’re good. If you don’t have a subtitle, then set the subtitle to hidden, and change the constant value of your titleLabel vertical constraint to 0 so it is centered. Q.E.D.
Here's the new code for the CustomCell class. Should have done this the first time around. 100% easier than the first solution I did (I was hopped-up on coffee). Hope this works for you.
// CustomCell.m
#import "CustomCell.h"
#interface CustomCell()
//IBOutlet to custom .xib file of UITableViewCell file
#property (weak, nonatomic) IBOutlet UILabel *titleLabel;
#property (weak, nonatomic) IBOutlet UILabel *subtitleLabel;
//This is the new outlet to the contraint.
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *titleLabelVerticalConstraint;
#end
#implementation CustomCell
+ (NSString *)nibName
{
return #"CustomCell";
}
+ (NSString *)resuseIdentifier
{
return #"CustomCellReuseIdentifier";
}
- (void)setTitle:(NSString *)title
{
_title = title;
self.titleLabel.text = self.title;
}
- (void)setSubTitle:(NSString *)subTitle
{
_subTitle = subTitle;
self.subtitleLabel.text = self.subTitle;
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
//1. If no subtitle, override current constraint
if (!self.subTitle) {
self.subtitleLabel.hidden = YES;
self.titleLabelVerticalConstraint.constant = 0;
}
}
#end
If it's a custom label, the easy fix here is to just use one label and add either 1 or 2 lines of attributed text. I just threw a project together and it works OK. I've put more explanation than you'll need (you already know how to subclass a table view cell, but for people who might need it, I've added a lot of detail).
Basically:
Subclass UITableViewCell and add a single label. Set your auto layout so the label is aligned vertically in the cell and set your left and right margins. You'll probably have to mess with auto layout to add padding to the top or bottom, but I'll leave the wrestling with auto layout to you lol
Create either 1 or 2 NSMutableAttributedStrings. If you have only 1 string, it will center in the label on one line, with auto layout fitting around the text. If you have two lines, just add a return character.
Note: This code assumes you will always have at least the title, with the subtitle being optional.
Here is the tableViewController.m code I used
#import "MyTableViewController.h"
#import "CustomCell.h"
#implementation MyTableViewController
- (void)viewDidLoad
{
[super viewDidLoad];
//1. Register custom nib
[self.tableView registerNib:[UINib nibWithNibName:[CustomCell nibName] bundle:nil] forCellReuseIdentifier:[CustomCell resuseIdentifier]];
//2. Set a suggested height for auto-layout
self.tableView.estimatedRowHeight = 45;
}
#pragma mark - UITableView datasource
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 2;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
//1. Create an instance of the custom cell
CustomCell *cell = (CustomCell*)[self.tableView dequeueReusableCellWithIdentifier:[CustomCell resuseIdentifier] forIndexPath:indexPath];
//2. Setting some filler text (this would come from your data object)
if (indexPath.row == 0) {
cell.title = #"A cell with only title text";
} else {
cell.title = #"First we have the title";
cell.subTitle = #"And here is the subtitle";
}
return cell;
}
And this is the .h and .m for the custom UITableViewCell. Remember to subclass your prototype cell in storyboard to this class.
// CustomCell.h
#import <UIKit/UIKit.h>
#interface CustomCell : UITableViewCell
+ (NSString *)nibName;
+ (NSString *)resuseIdentifier;
#property (strong, nonatomic) NSString *title;
#property (strong, nonatomic) NSString *subTitle;
#end
And finally the CustomCell.m. It's long, so you'll have to scroll through. Sorry about that.
// CustomCell.m
#import "CustomCell.h"
//1. Created a typeDef to identify which label we are dealing with
typedef NS_ENUM(NSInteger, CustomLabelType) {
CustomLabelTypeTitle = 0,
CustomLabelTypeSubtitle,
};
#interface CustomCell()
//IBOutlet to custom .xib file of UITableViewCell file
#property (weak, nonatomic) IBOutlet UILabel *customLabel;
#end
#implementation CustomCell
+ (NSString *)nibName
{
return #"CustomCell";
}
+ (NSString *)resuseIdentifier
{
return #"CustomCellReuseIdentifier";
}
- (void)setTitle:(NSString *)title
{
_title = title;
}
- (void)setSubTitle:(NSString *)subTitle
{
_subTitle = subTitle;
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
//1. Add a line return after the title if we have a subtitle
if (self.subTitle) {
self.title = [NSString stringWithFormat:#"%#%#",self.title, #"\n"];
//2. Set the title to allow for 2 lines of text
self.customLabel.numberOfLines = 2;
}
NSMutableAttributedString *attTitle;
NSMutableAttributedString *attSubTitle;
//3. Set the title
if (self.title)
attTitle = [self _mutableStringWithText:self.title type:CustomLabelTypeTitle];
//4. If we have a subtitle, append it to the title attributed string
if (self.title && self.subTitle) {
attSubTitle = [self _mutableStringWithText:self.subTitle type:CustomLabelTypeSubtitle];
[attTitle appendAttributedString:attSubTitle];
}
//5. Set your label
[self.customLabel setAttributedText:attTitle];
}
- (NSMutableAttributedString *)_mutableStringWithText:(NSString *)labelText type:(CustomLabelType)labelType
{
//1. Create attributes for title vs subtitle
UIFont *labelFont = [UIFont systemFontOfSize: (labelType == CustomLabelTypeTitle) ? 16 : 12];
UIColor *textColor = (labelType == CustomLabelTypeTitle) ? [UIColor blackColor] : [UIColor grayColor];
//2. Create the paragraph style you want, including line spacing
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
[paragraphStyle setLineSpacing:6.5];
paragraphStyle.alignment = NSTextAlignmentLeft;
//3. Create your attributes dictionary
NSDictionary *paragraphAttributes = #{NSParagraphStyleAttributeName: paragraphStyle, NSFontAttributeName: labelFont, NSForegroundColorAttributeName:textColor};
//4. Create and return your string
NSMutableAttributedString *mutableString = [[NSMutableAttributedString alloc] initWithString:labelText attributes:paragraphAttributes];
return mutableString;
}
#end

How do I get this UITableView to display?

I can't figure out how to get this UITableView to display.
The code creates the table fully programmatically except for the nib.
The nib is a simple cell that works in other UITableViews in the same app.
When the table view is created and added as a subview to the view and reloadData is called then numberOfSectionsInTableView: and tableView:numberOfRowsInSection: are both called. They both return 1. (I checked in the debugger.)
However tableView:cellForRowAtIndexPath: is never called.
I add the tableView to it's parent view with a NSLayoutConstraint that forces it's height, but nothing is displayed in that spot.
The view hierarchy debugger shows nothing in that spot and that there isn't a view on top of the table.
The app runs without any error or crashes.
I would appreciate your help.
#interface MyViewCell : UITableViewCell
#property (weak, nonatomic) IBOutlet UIButton *button;
#end
#implementation MyViewCell
#end
#interface MyDataTableEntry : NSObject
#property (nonatomic, strong) NSString* title;
#end
#implementation MyDataTableEntry
#end
#interface MyDataTable : NSObject <UITableViewDataSource, UITableViewDelegate>
#property (strong, nonatomic) UITableView *tableView;
#property (strong, nonatomic) NSArray *rows;
#property (strong, nonatomic) UINib* nib;
#end
#implementation MyDataTable
- (UINib*)getAndRegisterNib:(NSString*)reuseIdentifier {
UINib* nib = [UINib nibWithNibName:reuseIdentifier bundle:nil];
[self.tableView registerNib:nib forCellReuseIdentifier:reuseIdentifier];
return nib;
}
- (id)initWithRows:(NSArray*) rows andView:(UIView*)view {
if (self = [super init]) {
self.rows = rows;
self.tableView = [[UITableView alloc] initWithFrame:view.bounds style:UITableViewStylePlain];
self.nib = [self getAndRegisterNib:#"PrototypeCell"];
self.tableView.delegate = self;
self.tableView.dataSource = self;
self.tableView.backgroundColor = [UIColor grayColor];
}
return self;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.rows.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
MyViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"PrototypeCell" forIndexPath:indexPath];
MyDataTableEntry* entry = self.rows[indexPath.row];
[cell.button setTitle:entry.name forState:UIControlStateNormal];
cell.button.backgroundColor = [UIColor blueColor];
return cell;
}
#end
Here is a snippet that creates the table:
MyDataTableEntry* entry = [[MyDataTableEntry alloc] init];
entry.title = #"hello";
self.dataTable = [[MyDataTable alloc] initWithRows:#[entry] andView:self.view];
[self.view addSubview:self.dataTable.tableView];
[self.dataTable.tableView setNeedsLayout];
[self.dataTable.tableView layoutIfNeeded];
[self.dataTable.tableView reloadData];
Without confirming in a debugger:
The problem is here:
self.dataTable = [[MyDataTable alloc] initWithRows:#[entry] andView:self.view];
[self.view addSubview:self.dataTable];
self.dataTable is an NSObject. The second line should read something like this:
[self.view addSubview:self.dataTable.tableView];
I would also suggest that you engage in some refactoring. MyDataTable is not a view. It's a controller. So rename to MyDataTableController. Then the bug would have been more obvious.
I would also ask why you are doing this in code? you are already using nibs. So why not go to storyboards. Doing that and using UITableViewController as a base class should enable you to remove a lot of the code and potential bugs.

Animation for interactive keyboard in iOS 8

I have a view controller loaded from xib with a table view and a text field as titleView in the navigation bar. Simple.
#interface ViewController () <UITableViewDataSource, UITableViewDelegate>
#property (nonatomic, weak) IBOutlet UITableView *tableView;
#end
#implementation ViewController
#synthesize tableView;
- (void)viewDidLoad {
[super viewDidLoad];
tableView.keyboardDismissMode = UIScrollViewKeyboardDismissModeInteractive;
[tableView reloadData];
UITextField *tf = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, 320, 44)];
self.navigationItem.titleView = tf;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 42;
}
- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#""];
cell.textLabel.text = #"test cell";
return cell;
}
#end
I tap the text field, let the keyboard show and start to drag the table view to interact with keyboard.
The problem is when I release touch while interacting and let the keyboard fully show: it goes up without animation. If I release to hide it works.
Furthermore this is only in iOS 8. It works in iOS 7.
Am I missing anything?

how can i add multiple views depending on an array size?

im making an app to sell tickets, there are different ticket types, for each ticket type i would like to ass a:
nameLabel UIStepper amountLabel priceLabel.
so those 4 views * ticket types.. added to the viewcontroller during runtime.
can i do this without doing it in a tableviewcontroller?
cant seem to add them dynamicly below eachother, any hints?
Im assuming your ticket is a UITableViewCell since you are mentioning "tableviewcontroller".
If thats the case your should make a subclass of UITableViewCell and add those 4 views to the subclassed cell.
So that your subclassed cell header would look something like:
#Import <UIKit/UIKit.h>
#interface TicketCell : UITableViewCell
#property (weak, nonatomic) IBOutlet UILabel *nameLabel;
#property (weak, nonatomic) IBOutlet UIStepper *stepper;
#property (weak, nonatomic) IBOutlet UILabel *ammountLabel;
#property (weak, nonatomic) IBOutlet UILabel *priceLabel;
#end
Then you should set this class as your UITableViews prototype cell class in Interface Builder and then drag and drop the UILabels and the UIStepper into the prototype cell. After that you need to connect the outlets to the right UILabels and UIStepper.
And in your uitableviewcontroller you want to reuse this prototype
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellId= #"PrototypeCellStoryboardIdentifier";
TicketCell *cell = [tableView dequeueReusableCellWithIdentifier:cellId];
if (cell == nil) {
cell = [[TicketCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellId];
}
return cell;
}
and finally you want to set the number of rows in your table to the number of objects in your "number-of-ticket-types-array" like this:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [ticketTypes count];
}
if you don't want to use tableview you can do this by using UIScollView as follows
import "ViewController.h"
#interface ViewController ()
{
float y;
}
#property (weak, nonatomic) IBOutlet UIScrollView *scrollView;
- (IBAction)newTicket:(UIButton *)sender;
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
y=50.0;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)newTicket:(UIButton *)sender
{
UILabel * nameLabel = [[UILabel alloc]initWithFrame:CGRectMake(10.0,y,50, 30)];
UILabel * priceLabel = [[UILabel alloc]initWithFrame:CGRectMake(10.0,y+32, 50, 30)];
UILabel * amountLabel = [[UILabel alloc]initWithFrame:CGRectMake(10.0,y+64, 50, 30)];
UIStepper * stepper = [[UIStepper alloc]initWithFrame:CGRectMake(10.0,y+96,50,30)];
nameLabel.text = #"name";
priceLabel.text = #"price";
amountLabel.text = #"amount";
[self.scrollView addSubview:nameLabel];
[self.scrollView addSubview:priceLabel];
[self.scrollView addSubview:amountLabel];
[self.scrollView addSubview:stepper];
y = y +130.0;
if (y>=self.view.frame.size.height)
{
self.scrollView.contentSize =CGSizeMake(320.0, y+120.0);
}
}
#end

Resources