I have a webview in a uitableview cell. The problem is that the web view only takes up 75% of the uitableview cell
I am not sure how iOS decided the height of the web view, as I can't seem to increase how much space it takes. I can increase the tableview cell's height higher or lower, but the web view does not increase.
One thing I imagined I needed to do was somehow get the height of the web view itself based on how big the text is so that it can automatically scale the size of the tableview cell.
a) I'm not sure what function retrieves it
b) I think this would go in (void)webViewDidFinishLoad:(UIWebView *)webView method, but I can't get the app to go into that function. I tried debugger and it seems not to go in there at all. (web view object is within its own controller)
#import "WebViewCell.h"
NSUInteger const WEB_VIEW_ROW_HEIGHT = 75;
#implementation WebViewCell
#synthesize webView;
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code
}
return self;
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
#end
and in the controller which actual populates the tableview cell with the web view:
WebViewCell *cell = [[ViewManager instance] loadWebViewCell:self.tableView cellID:eventDetailsValueCellID];
NSString *htmlText = [news valueForKey:propertyName];
[cell.webView loadHTMLString:htmlText baseURL:nil];
return cell;
I'm not sure where to put the (void)webViewDidFinishLoad:(UIWebView *)webView method with this setup. Does it go in the WebViewCell.m or the controller that calls WebViewCell.m
also,the root question is how can I make the web view scale to the size of the cell?
The (void)webViewDidFinishLoad:(UIWebView *)webView needs to be implemented by the UIWebViewDelegate.
Try adding cell.webView.delegate = self in the cellForRowAtIndexPath method and also adding the delegate protocol 'UIWebViewDelegate' to your controller's header file.
This should at least get your (void)webViewDidFinishLoad:(UIWebView *)webView call to fire allowing you to do your height calculation after the web view has loaded.
The calculation would probably look something like:
webView.frame = CGRectMake(0, 0, webView.superview.frame.size.width, webView.superview.frame.size.height);
Related
I am trying to insert a custom UITableViewController inside a Container View. The Container View is placed inside a cell of a static UITableView as shown in the figure below.
http://i.stack.imgur.com/A762B.png
I simply want a method to combine static with dynamic cells in the same screen.
In the Identity Inspector when the field Class is empty (i.e. a standard UITableViewController) it works showing an empty dynamic table inside the cell. But when I put my custom class name (that extends UITableViewController) in that field I get a NSInternalInconsistencyException:
[UITableViewController loadView] loaded the "Enx-aT-Rum-view-zY2-9U-Z6d" nib but didn't get a UITableView.
These are the contents of MyCustomUITableViewController:
#implementation MyCustomUITableViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Uncomment the following line to preserve selection between presentations.
// self.clearsSelectionOnViewWillAppear = NO;
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 2;
}
#end
I have to admit that I still don't understand all the logic behind Container View, but I just want to show only one view inside (not doing any swapping or else).
Any help would be appreciated, thanks!
Let me point out the issues here first-
Problem 1: you can not have an controller inside another controller. That means, inside your Static tableview, you can not have a dynamic tableview Controller.
Solution 1:You can have a dynamic TableView.
Problem 2: You can not have a static Tableview inside a View Controller. If you want a Static Tableview then you need to have a UITableViewController rather than the UIViewController.
Solution 2: You just need to delete the ViewController you have and replace with a UITableViewController.
Now to achieve one controller where you can have a dynamic TableView inside a static tableview, you will need to implement you dynamic table's datasource inside the Static Table's ViewController which would be a very bad practise.
The static table should not need to know anything about your dynamic Table, at least about the data that will be populated in your dynamic table. However, if you want a dynamic tableview inside your static tableView then your static tableview's controller needs to implement the UITableViewDatasource.
So, you may want to re-think about the structure.
I am trying to insert a custom UITableViewController inside a Container View. The Container View is placed inside a cell of a static UITableView as shown in the figure below.
I simply want a method to combine static with dynamic cells in the same screen.
In the Identity Inspector when the field Class is empty (i.e. a standard UITableViewController) it works showing an empty dynamic table inside the cell. But when I put my custom class name (that extends UITableViewController) in that field I get a NSInternalInconsistencyException:
[UITableViewController loadView] loaded the "Enx-aT-Rum-view-zY2-9U-Z6d" nib but didn't get a UITableView.
These are the contents of MyCustomUITableViewController:
#implementation MyCustomUITableViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Uncomment the following line to preserve selection between presentations.
// self.clearsSelectionOnViewWillAppear = NO;
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 2;
}
#end
I have to admit that I still don't understand all the logic behind Container View, but I just want to show only one view inside (not doing any swapping or else).
Any help would be appreciated, thanks!
After banging my head on the desk for so many hours I managed to resolve this.
I had to alloc/init the tableview of MyCustomUITableViewController. I override the loadView method of MyCustomUITableViewController:
- (void) loadView{
self.tableView = [[UITableView alloc]init];
}
And it is good to go!
I created a custom UITableViewCell , but want to use the standard UIImageView that is in the cell.
I notice that the frame is always zeroed , which is causing me problems
#implementation MyCustomCell
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
// the style is "default" style
self = [super initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseIdentifier];
// I need to override the cells image
if (self) {
self.imageView.image = [UIImage imageNamed:#"MyImageToUseInThisCell"]; // The image view frame is {0,0,0,0}
}
return self;
}
You won't get any frame sizes in the -initWithStyle:reuseIdentifier: because the cell hasn't loaded yet which means the subviews haven't been created yet (i.e. are nil) and hence a 0 frame size.
Even for a viewController, you won't get frame size in the -initWithNibName: method.
You would be better off with:
Frame specific related stuff in -layoutSubviews
Setting an initial property in -awakeFromNib (or directly via IB)
Anyways, try this:
//does this method even run? it shouldn't for a custom UITableViewCell
//unless you've mixed up things unnecessarily
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
NSLog(#"initWithStyle -- %#",self.myImageView);
}
return self;
}
//do frame related stuff here
-(void)layoutSubviews
{
//fyi: this method is called anytime any frame changes
NSLog(#"layoutSubviews -- %#",self.myImageView);
}
- (void)awakeFromNib
{
//fyi: this method is called when the view is pulled from the IB
// Initialization code
NSLog(#"awakeFromNib -- %#",self.myImageView);
//do you thang here
[self.myImageView setImage:[UIImage imageNamed:#"MyImageToUseInThisCell"]];
}
-layoutSubviews on SO
-awakeFromNib on SO
Seems that the problem is when dequeuing a custom cell with a reuse identifier and index path the custom cell base class doesn't set those properties. This is not mentioned in the docs which is very unfortunate.
I make a view like imessage, just input text into the bottom text view. I use table view to do this, and the text view in the last cell. when I input long text that more than one line, I need the text view and the cell become tailer. so I need refresh cell's height. but if I use table view's reload or reload row, the content in text view will disappear and the keyboard will disappear too. Is there any way better to fix it?
May be I should use tool bar to do it easy? but I still doubt table view can do it.
The cells will resize smoothly when you call beginUpdates and endUpdates. After those calls the tableView will send tableView:heightForRowAtIndexPath: for all the cells in the table, when the tableView got all the heights for all the cells it will animate the resizing.
And you can update cells without reloading them by setting the properties of the cell directly. There is no need to involve the tableView and tableView:cellForRowAtIndexPath:
To resize the cells you would use code similar to this
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
NSString *newText = [textView.text stringByReplacingCharactersInRange:range withString:text];
CGSize size = // calculate size of new text
if ((NSInteger)size.height != (NSInteger)[self tableView:nil heightForRowAtIndexPath:nil]) {
// if new size is different to old size resize cells.
// since beginUpdate/endUpdates calls tableView:heightForRowAtIndexPath: for all cells in the table this should only be done when really necessary.
[self.tableView beginUpdates];
[self.tableView endUpdates];
}
return YES;
}
to change the content of a cell without reloading I use something like this:
- (void)configureCell:(FancyCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
MyFancyObject *object = ...
cell.textView.text = object.text;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
FancyCell *cell = (FancyCell *)[tableView dequeueReusableCellWithIdentifier:#"CellWithTextView"];
[self configureCell:cell forRowAtIndexPath:indexPath];
return cell;
}
// whenever you want to change the cell content use something like this:
NSIndexPath *indexPath = ...
FancyCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
[self configureCell:cell forRowAtIndexPath:indexPath];
I've written a subclass of UITableViewCell to handle this functionality.
.h file:
#import <UIKit/UIKit.h>
#protocol AECELLSizeableDelegate;
#interface AECELLSizeable : UITableViewCell
#property (weak, nonatomic) id <AECELLSizeableDelegate> delegate;
#property IBOutlet UIView *viewMinimized;
#property IBOutlet UIView *viewMaximized;
#property BOOL maximized;
#property CGFloat height;
- (IBAction)clickedConfirm:(id)sender;
- (IBAction)clickedCancel:(id)sender;
- (void)minimizeForTableview: (UITableView*)tableView;
- (void)maximizeForTableview: (UITableView*)tableView;
- (void)toggleForTableview: (UITableView*)tableView;
#end
#protocol AECELLSizeableDelegate <NSObject>
- (void)sizeableCellConfirmedForCell: (AECELLSizeable*)cell;
- (void)sizeableCellCancelledForCell: (AECELLSizeable*)cell;
#end
.m file:
#import "AECELLSizeable.h"
#implementation AECELLSizeable
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code
}
return self;
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
- (void)minimizeForTableview: (UITableView*)tableView
{
self.maximized = NO;
[self.viewMinimized setHidden:NO];
[self.viewMaximized setHidden:YES];
self.height = self.viewMinimized.frame.size.height;
[tableView beginUpdates];
[tableView endUpdates];
}
- (void)maximizeForTableview: (UITableView*)tableView
{
self.maximized = YES;
[self.viewMinimized setHidden:YES];
[self.viewMaximized setHidden:NO];
self.height = self.viewMaximized.frame.size.height;
[tableView beginUpdates];
[tableView endUpdates];
}
- (void)toggleForTableview:(UITableView *)tableView
{
if (self.maximized) {
[self minimizeForTableview:tableView];
} else {
[self maximizeForTableview:tableView];
}
}
- (void)clickedConfirm:(id)sender
{
[self.delegate sizeableCellConfirmedForCell:self];
}
- (void)clickedCancel:(id)sender
{
[self.delegate sizeableCellCancelledForCell:self];
}
#end
Example Usage:
Create a UITableViewController with a static UITableView in IB
Add a cell to the tableview that is a AECELLSizeable (or subclass of it)
Create two UIViews in this cell. One UIView will be used for the content visible while minimized, the other will be used for the content visible while maximized. Ensure these cells start at 0 on the y axis and that their height is equal to that of the height you wish to have the cell for each state.
Add any subviews to these two views you desired. Optionally add confirm and cancel UIButtons to the maximized UIView and hook up the provided IBActions to receive delegate callbacks on these events.
Set your tableview controller to conform to the AECELLSizeableDelegate and set the cell's delegate property to the tableview controller.
Create an IBOutlet in your UIViewController's interface file for the AECELLSizeable cell.
Back in IB, ensure the cell's initial height is that of the minimized version and connect the IBOutlet you just previously created.
Define the tableview's heightForRowAtIndexPath callback method in the tableview controller's implementation file as such:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.row == 0) {
//Sizeable Cell
return self.cellSizeable.height;
} else {
return [super tableView:tableView heightForRowAtIndexPath:indexPath];
}
}
In your tableview controller's viewDidLoad method, call minimizeForTableview on your cell to ensure it starts off minimized.
Then, call maximizeForTableview: on the cell when it is selected via the didSelectRowAtIndexPath callback from the tableview (or however else you would like to handle it) and call the minimizeForTableview: method on the cell when you receive the canceled / confirmed delegate callbacks from the cell (or, again, however else you'd like to handle it).
Check out this library. This is an implementation of message bubbles using UITableView. Keep in mind that every time a cell is displayed, cellForRow:atIndexPath is called and the cell is drawed.
EDIT
You can use heightForRowAtIndexPath
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
Messages *message = [self.messageList objectAtIndex:[indexPath row]];
CGSize stringSize = [message.text sizeWithFont:[UIFont fontWithName:#"HelveticaNeue-Light" size:13]
constrainedToSize:CGSizeMake(320, 9999) lineBreakMode:UILineBreakModeWordWrap];
return stringSize.height + 78;
}
Table view cells won't smoothly resize. Dot.
However, since your text view is in the last cell, you are lucky because you can easily simulate a row resizing. Having a text view in the middle of the table would be much more difficult.
Here is how I would do it: put your text view on top of the table view. Sync its position with the contentOffset of the tableView in scrollViewDidScroll:. And use the animatable contentInset of the tableView in order to leave room for the textView, as the user types in it. You may have to make sure your textView is not scrollable.
I have a UIViewController with an embedded tableview.
The prototype cell has been customized (and subclassed) to add a "slider" effect started by a pan gesture recognizer.
I've followed this tutorial to do it: https://github.com/spilliams/sparrowlike
Inside the cell I've got two views, a front view that slides away, and a back view that stays there showing other controls.
Now I need to add an image to every cell, this image will be chosen between two images depending on a BOOL variable.
The problem is: if I add the image view programmatically, the image will be added to the cell and then when the user swipes away the front view, the image stays there.
So the image should be added to the front view, but I can't do it in the StoryBoard and add an outlet to it.
So the question is: should I put the code to retrieve the image in the custom cell subclass?
If so, where should I put it?
My "CustomCell" class implementation file looks like this at the moment:
#import "CustomCell.h"
#implementation CustomCell
#synthesize frontView;
#synthesize backView;
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
}
return self;
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
#end
What about something like this?
- (void)setupCell {
if (yourBoolValueHere) {
[[self frontView] setImage:[UIImage imageNamed:someImageName1]];
} else {
[[self frontView] setImage:[UIImage imageNamed:someImageName2]];
}
[[self view] bringSubviewToFront:[self frontView]];
}
Then, you can just call [cell setupCell] in tableView:cellForRowAtIndexPath:.