I have a very peculiar situation and want to know why its happening. I have an edit box and button in a view that I put into a tableview's tableHeaderView property. I call this view InputToListCell.
Now when the view extends UIView I get the following:
i.e all I change in the code is:
class InputToListCell: UIView{
//Code Here
}
I then change InputToListCell to extend UITableViewCell and get the following:
i.e
class InputToListCell: UITableViewCell{
//Code Here
}
Why is this behaviour occurring? I cant see why because UITableViewCell extends UIView. Any thoughts?
Update:
Based on a comment made here are the constraints for the InputToListCell:
I basically pin both the edit text's and Add buttons constraints to the margin, except for the edit text's trailing value.
In short
Use UITableViewCell for table view elements.
self.tableView.tableHeaderView =
tableView.dequeueReusableCellWithIdentifier("sectionIdentifier")
Discussion
Using UITableViewCell objects for all table view elements not only provide an umbrella behavior when presented inside a UITableView, but will also allow loading from a .storyboard or .xib, an added bonus and cohesion.
Thus, even though tableHeaderView (and viewForHeaderInSection for that matter) is informally just a UIView, you can return a UITableViewCell. According to this post, to take advantage of Autolayout in tableHeaderView, you need to do a bit of gymnastics:
if let tableHeaderView = tableView.dequeueReusableCellWithIdentifier("tableHeaderViewIdentifier") {
tableHeaderView.setNeedsLayout()
tableHeaderView.layoutIfNeeded()
let height = tableHeaderView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height
var frame = tableHeaderView.frame
frame.size.height = height
tableHeaderView.frame = frame
self.tableView.tableHeaderView = tableHeaderView
}
From a different post:
For a complete example on how to use .storyboard or .xib for headers, including an Xcode project, see https://stackoverflow.com/a/32261262/218152
Related
I have simple application which contains UICollectionViewController with custom UICollectionViewCell. The controller is placed in Mainstoryboard (I use native xamarin). My application works, but I want to add a common 'header' control above of my collection view (with buttons and labels which will work with/describe collection).
I had expected, it can be done by UIStackView when my already created collection view would be in the bottom of StackView and my newly creted 'header' control in the top. But as I may see, I cannot add my UICollectionViewController as item of Stack view.
I think it can be done by TabBarController, but it looks like it's not a good idea, especially If i have more then one a potencial control buttons for my collection view.
Maybe there is a better solution?
Could someone advise me?
You can Create a new UIViewController, put a UIStackView in it. Then initialize your UICollectionViewController in Storyboard, add its view in the StackView:
UIView headerView = new UIView();
...//Add some items in this view
//Add a height constraint
headerView.AddConstraint(NSLayoutConstraint.Create(headerView, NSLayoutAttribute.Height, NSLayoutRelation.Equal, null, NSLayoutAttribute.NoAttribute, 1, 200));
MyStack.AddArrangedSubview(headerView);
MyCollectionView collection = Storyboard.InstantiateViewController("MyCollectionView") as MyCollectionView;
MyStack.AddArrangedSubview(collection.View);
But if you only want to add a header view, I recommend you to use UICollectionView's Header.
Firstly, Use the event below to create the header in the CollectionView's
Source:
public override UICollectionReusableView GetViewForSupplementaryElement(UICollectionView collectionView, NSString elementKind, NSIndexPath indexPath)
{
if (elementKind == "UICollectionElementKindSectionHeader" && indexPath.Section == 0)
{
UICollectionReusableView header = collectionView.DequeueReusableSupplementaryView(new NSString("UICollectionElementKindSectionHeader"), "MyHeader", indexPath);
//Add some items
//header.AddSubview();
return header;
}
return null;
}
Do not forget register the Header Class: CollectionView.RegisterClassForSupplementaryView(typeof(UICollectionReusableView), new NSString("UICollectionElementKindSectionHeader"), "MyHeader");
At last set the Header's height to show it:
// This event exists in the UICollectionViewDelegateFlowLayout
public override CGSize GetReferenceSizeForHeader(UICollectionView collectionView, UICollectionViewLayout layout, nint section)
{
if (section == 0)
{
return new CGSize(UIScreen.MainScreen.Bounds.Size.Width, 300);
}
return CGSize.Empty;
}
You can add your UICollectionView as a subview to UIStackView by creating a UIViewController in storyboard and just drag and drop UIStackView and UICollectionView as a subview inside it. and assign the View Controller as the Collection View’s data source property
For Common header control, you might wanna a create a custom UIView using either XIB or by programmatic approach by creating a sub class of UIView.
so your controller hierarchy would be
->UIViewController
->UIStackView
->UIView
->UICollectionView
However, UIStackView is ideal for situation where you have a complex view hierarchy where you don't have to deal with multiple auto layout constraints by yourself, but it has some common issues you might want to look at this post.
If that's not the case then you should use a simpler approach and just add constraints manually to layout your views.
I'm having a problem with my table view cells as they do not adjust automatically with its content.
I have a label for a title and another label for a name. There is a text view below the two labels which is never displayed when the simulator runs.
This is
what the Table View Cell is supposed to look like, however, this is what the Table View Cell displays.
I have pinned all elements inside the table view cell to the content view using constraints. I read up that adjusting the table view cell height itself will not work, so, I have to adjust the height from the table view itself.
It is set to automatic but it is not adjusting as seen here. I have also tried to set the estimated height to automatic but to no avail. The only solution was to set a custom height but it would look extremely weird if the text view contains only a few text as there would be a large white space. I did not add any code at all to adjust the size.
These are the following constraints:
Table View
Name Label
Title Label
Text View
First You need to add height constraint for textview and add its IBOUTlet then you need to override the updateconstraint of cell and do following in update constraints method.
override func updateConstraints() {
super.updateConstraints()
self.textViewHeightConstraint.constant = self.textView.contentSize.height
}
and also for name label add bottom constraint.
By default the UITextView will not resize itself to fit its content. While you could use #Waqas Sultan approach, I would recommend to let the textView size itself. To achieve that, just use this:
textView.isScrollEnabled = false
Or, in storyboards, find the Scroll Enabled attributed and uncheck it.
This would make textView to size itself.
However, from the constraints you show it is hard to tell if there are really enough constraints to determine the proper frames for all the content - there are constraints related to Review label, but who knows how you constrained that label.
Not seeing all the relevant constraints in the tableView cell, I cannot guarantee that this will be enough to make it work as you expect (you might have forgotten about just a single one constraint, and it might be messing up your whole layout).
Hey buddy i would like you to try this way.
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath.row == desiredIndexPath { // the index where you want automatic dimension
return UITableViewAutomaticDimension
} else {
return 100 // the height of every other cell.
}
}
note: Make sure that you do not give height to the label. Otherwise the label wont expand according to content.
I have created a test project in order to verify this problem. In the project storyboard I have two scenes. One scene is based on the initial ViewController that was added to the single view application when the project was created. The second scene was created by dragging a UITableViewController onto the storyboard.
In both scenes, I believe I have configured the prototype cell so it should auto size. When I set the initial ViewController to the one based on the UIViewController, the UITableViewCell do not show / size correctly. (See below)
However, when I set the initial ViewController to the one based on the UITableViewController, everything works fine. (see below)
The code / project is very simple and I am hoping someone has seen this and can tell me why the auto size for the UITableViewCell is not working correctly when the class is based on UIViewController verses a UITableViewController. Any help would be greatly appreciated. It appears I cannot upload the project here so I will try to get it uploaded to another location and update the question with a link.
To answer standard questions
Both prototype cells have two UILabel in them
The first UILabel has left, top, right and bottom constraints specified.
The second UILabel has left, right and bottom constraints specified.
Both classes set the estimatedRowHeight to a number and the rowHeight to UITableViewAutomaticDimension
All UILabel have their lines property set to 0
The first UILabel in both scenes has its Line Breaks set to Word Wrap
The second UILabel in both scenes has its Line Breaks set to Truncate Tail.
Below is a screen shot of the scene in interface builder. Left is based in UIViewController, right is based on UITableViewController
Here is a link to the code I hope: Sample Project
So I have found the "silver bullet" that seems to resolve a great number of challenges regarding auto layout of prototype cells.
In the cellForRowAtIndexPath function I added the following line of code right before I return the cell:
lobj_NearbyLocationEntry!.layoutIfNeeded()
(lobj_NearbyLocationEntry is the name of my cell variable I am returning.)
When I do this, the auto layout for my table works fine. On a side note, I found a defect in the code that uses the UITableViewController also. Once the data loads and it all looks good, if you scroll down and then scroll back up, you will see a couple of the cells are now not laid out correctly. Putting this line of code in that view controller's code also resolved that layout problem.
I hope this helps many people who are sitting there trying to figure out why their app is not auto laying out a tableView cell correctly. :)
For Me It Works
Give upper UILabel (right,top,left) constraints
Give lower UILabel (right,top,left,bottom) constraints
In Custom UITableViewcell
override func awakeFromNib() {
super.awakeFromNib()
up.lineBreakMode = NSLineBreakMode.ByWordWrapping // Label outlet
down.lineBreakMode = NSLineBreakMode.ByWordWrapping // Label outlet
up.numberOfLines = 0
down.numberOfLines = 0
// Initialization code
}
EDIT
InUITableViewController
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
In UIViewController
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
I am making app in which there is a chat window. In this window there is one image and a label on that image in custom cell.
I have take two custom cells, one for sender and other for receiver. Both cell are same with left and right alignment.
I want that when the length of comment is increased then whole comment shows in multiline within that image(increases the image size also) .
how can I handle this situation?
I am using setVariable method to set content on cell. I am trying comment code for framing like below comment code but it doesn't work
- ( void ) setComment : ( NSString* ) Comment
{
[ txtComment setText : Comment ] ;
/*CGRect frame1 = txtComment.frame;
frame1.size.height = txtComment.contentSize.height;
txtComment.frame=frame1;*/
}
I would suggest you use auto layout to define the custom cell height. It will help you create a dynamic cell height depending on the length of the comment. You can read about using dynamic height using auto layout in this link
To make this done you can do the following:
Subclass UITableViewCell and also create xib file.
Go to the xib file and add UIImageView and UILabel objects to your cell. Also create an outlet for the label.
As Pavan Kotesh mentioned the easiest way is to use auto layout.
Add top space and bottom space constraints to the cell content view for both image view and label. Then set constraints for x position for both subviews and finally set width and height constraints.
Height constraints must be "Greater of equal" type to let you change size of the views.
Having done that in Interface Builder all you need is to add one method to your subclass for setting a message.
- (void) setMessage: (NSString*) message
{
CGFloat oldLabelSize = _label.frame.size.height;
_label.text = message;
[_label sizeToFit];
CGFloat newLabelSize = _label.frame.size.height;
CGRect frame = self.frame;
frame.size.height += newLabelSize - oldLabelSize;
self.frame = frame;
}
After calling this method add the cell as subview to you chat view.
EDIT:
I think your -cellForRowAtIndexPath method implementation is wrong. What does [ChatViewCell send] perform?
I would have done it like this:
First declare two arrays in your table view controller class. The first is for storing the text of messages and the second for storing cell heights. Also you can create some structure to store those values. After user has finished inputing a message you should somehow estimate cell height and put both height and cell's message to the appropriate arrays. After that you should insert new row(section) in your tableview using insertRowsAtIndexPaths:withRowAnimation: method.
Then in your cellForRowAtIndexPath method:
If dequeueReusableCellWithIdentifier returns nil you should just initialise new ChatViewCell and after if (cell == nil) statement set its message from the appropriate array (due to reuse you should set cell's content every time it goes on screen).
In your heightForRowAtIndexPath: method return values from array that stores cell heights.
What I have in the view controller view are :
An image of fixed height
Few labels
Table view with n rows.
Once rendered I want everything here to be inside the scroll the view so the user can scroll the entire screen as needed. Note that the scrollView needs to expand to the entire size of the tableView to show its full contents. I have tried different ways of doing this but unable to do it. I would appreciate any pointers or code segment to get this done.
There are essentially two ways to do so.
tableHeaderView
The first way involves the tableHeaderView property of the UITableView instance you have. You can simply add the UITableView with the constraints/frame/autoresizingMask that allows you to put it full-screen. Done that, you simply do (i.e. in your viewDidLoad):
UIView *headerView = [UIView new];
// Here I am supposing that you have a 200pt high view and a `self.tableView` UITableView
headerView.frame = CGRectMake(0, 0, self.tableView.frame.size.width, 200.0);
UIImageView *fixedImageView = [UIImageView new];
// configure your imageView..
[headerView addSubview:fixedImageView];
// configure labels as you want and add them to headerView as subviews
// Now set `UITableView` headerView
self.tableView.tableHeaderView = headerView;
If you want to use AutoLayout for your tableHeaderView, I suggest you to take a look at this question
Dynamic scrollView
Another way to do this is to to create an UIScrollView, put everything inside, and let it scroll. The downside of this method is that if you are using floating section headers for your UITableView, they will not float due to the fact that the tableView is going to stay fixed, while the parent scrollView is going to scroll.
On the other side, this approach is more AutoLayout friendly due to the fact you can use constraints easily.
To do so, you start adding an UIScrollView to your view, and placing all your other views inside it.
Be sure to add a Vertical Spacing constraint between the first view inside your scrollView (I suppose the UIImageView) and the scrollView top, and between the last view (I suppose the UITableView) and the scrollView bottom, to avoid an ambiguous content size.
You should have something like that (I omitted the labels for the sake of brevity):
Note that every view is inside a parent UIScrollView
After that, add an Height constraint to the tableView, and add an IBOutlet to your view controller subclass, i.e. like this:
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *tableViewHeightConstraint;
Now you only need to configure this constraint to reflect the tableView natural height, given by its rows, etc. To do so, you simply calculate the height in this way:
// Resize TableView
CGFloat height = self.tableView.contentSize.height;
self.tableViewHeightConstraint.constant = height;
Now the tableView will resize, and due to its constraints it will adapt the parent scrollView contentSize.
Just be sure to refresh this height constraint anytime you reload the UITableView dataSource.