Context I have a UITableViewController with custom cells designed in a .xib file. In that .xib file I'm trying to set dynamic widths so splits the cell width into three subviews.
Problem is that the cell doesn't seem to be able to get the proper width, causing the cell to not fill the entire width. Here's a picture to better illustrate what I'm saying.
cell_not_filling_width
My Understanding (Could be totally misunderstanding this) Setting the frame constraint in the .xib file is no good b/c the program won't know the width until runtime. In other words, awakeFromNib (1.) loads before the screen width is determined... So idk how to fix this problem.. Any guidance would be greatly appreciated.
Presumably this is where the width is set - the width on my interface builder when I'm looking at the .xib file is the same when I print it out at cellForRowAt
Notes In my viewDidLoad function I've registered the nibfile as such so I don't think that's the problem. tableView.register(UINib(nibName: "NewTableViewCell", bundle: nil) , forCellReuseIdentifier: "newTableViewCell").
Here is some code that could be relevant.
NewTableViewCell.swift
import UIKit
class NewTableViewCell: UITableViewCell {
#IBOutlet var typeLabel: UILabel!
#IBOutlet var timeLabel: UILabel!
#IBOutlet var completedLabel: UILabel!
#IBOutlet var correctLabel: UILabel!
#IBOutlet var parentView: UIView!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
}
The parentView is a view that contains the three subviews.
Here'sa picture of the .xib file
Here's a picture of my constraints on the .xib file (it seems cluttered)
UPDATE
Tried the stack view approach: XCode just defines the width as the subview with the biggest width (according to how large the text is)
Here is a picture of the current situation and the current constraints
I figured the constraints on the label may also be relevant:
UPDATE2
Ok I think I've found the problem; Basically the stack view cannot seem to detect the super view's length and width and it's dimension.
NOTE I have edited the constraints.
Here is what it looks like if I set the width and height to a constant value. The stack view will actually go to the set constraints
Here is what it looks like if I take those constraints out. So it seems like the width and height are 0. Which is why I believe the constraints are not being adopted from the super view.
Does anyone know how to fix this?
Try the below code in cellforRow.
Add IBOutliet for stackviewWidth and set is dynamically as shown below. Also check if the cell goes to the full width of the device by adding background color to cell.
if let appDelegate = UIApplication.shared.delegate as? AppDelegate,
let window = appDelegate.window {
cell.stackViewWidthConstraint = window.bounds.width
}
Related
The Collection view cell would need to show and hide additional tools for the device being controlled. The button that collapses would be at the bottom of the UICollectionViewCell. Whenever the button is pressed it would animate downwards revealing the fan or lights extra features or animate upwards when hiding the extra features. I only need help with expanding and collapsing by using the button at the bottom of the cell. An image is attached showing an example.
Add a height constraint for your high/med/low selector.
Connect that constraint to var in your cell class like this:
#IBOutlet weak var checker1HeightConstraint: NSLayoutConstraint!
set that constraint to a value you want (either 0, for hidden, or 50 or 60 if not hidden)
have a method where you set the value:
func toggleControls(visible: Bool)) {
checker1HeightConstraint = visible? 50 : 0
setNeedsLayout()
}
If you are not sure how to set the outlet, just ctrl-drag from the constraint in you storyboard or xib to your custom cellView class. It will give you options to create it.
For example, I use this code to change the frame position of a UIView called Propo:
self.Propo.frame.origin.x -= self.view.frame.width
(Propo has constraints in Storyboard)
But When the app view disappears and reappears, the UIView resets itself to its original position. How can I do to resolve it? I want the UIView to keep its position when I change it programmatically.
I've tried updateViewConstraints() by anything else append...
Constraints and manual frame manipulation are directly in conflict. You have to choose one or the other. You should instead "promote" the constraint you wish to change by Ctrl-dragging it to your view controller as an #IBOutlet in Storyboard. Then, you can manipulate its constant:
class MyViewController: UIViewController {
#IBOutlet weak var myPropoLeftEdgeConstraint: NSLayoutConstraint!
#IBAction doChangePropo(sender: AnyObject) {
myPropoLeftEdgeConstraint.constant -= view.frame.width
view.layoutIfNeeded()
}
}
This is already covered in Objective C in many places, so just do a search on programmatically changing an NSLayoutConstraint in code if you can read those examples.
How to edit constraint in code
(Edit to make more applicable: generally constraints should be animated for a clean user experience. Otherwise the eye is confused.)
You can easily animate the constraint adjustment by wrapping view.layoutIfneeded in a UIView.animateWithDuration(duration) { ... } block.
How to make a UIStackView re-distribute it's sub-UITableViews while the stackView is inside a scrollview?
My layout hierarchy is based on the official documentation from apple about Dynamic content for StackViews
- UISCrollView
- UIStackView
- UIView A
- UIView B
- UIView C
- UITableView X
- UITableView Y
- UIView D
The constraints are set as documented. The initial layout of the StackView is correct showing all visible subviews. When forcing the regular views to expand beyond the screen's height, scrolling is working as expected. Also when viewing the layout in the storyboard, everything stacks as expected.
At this point the UITableViews are empty. As soon as I add content to the tableView the problem appears.
The problem
When I dynamically update the TableView's by calling .reloadData() on both of them I see their content appearing. (thanks to this answer about non-scrolling tableViews) but the UIStackView is not stacking the UITableViews.
UIView D is stacked below UIView C
UITableView X and UITableView Y also stacked below UIView B
My guess is that I need to invalidate the stackview, or somehow get it to redistribute it's subviews. How can I do this?
First, a warning:
What you're trying to achieve is not really standard iOS behavior. You should first consider a different approach like creating a single grouped table view with multiple sections. You can implement custom views inside your table view as section headers or footers.
Now if you really wanna go with your original approach...
... for some important reason you should be aware that a table view doesn't have an intrinsic content size by default. Thus, you need to tell the table view how tall it should be because otherwise it will only shrink down to a zero height.
You can achieve this by either subclassing UITableView and overriding its intrinsicContentSize() as Rob suggests in this answer to a similar question.
Or you add a height constraint to each of your table views and set their constants dynamically in code. A quick example:
Add both your table views to a vertical stack view in Interface Builder.
Give both table views a leading and a trailing constraint to pin their left and right edges to the stack view.
Create outlets for your table views in the respective view controller:
#IBOutlet weak var tableView1: UITableView!
#IBOutlet weak var tableView2: UITableView!
#IBOutlet weak var tableView1HeightConstraint: NSLayoutConstraint!
#IBOutlet weak var tableView2HeightConstraint: NSLayoutConstraint!
Override the updateViewConstraints() method of that view controller:
override func updateViewConstraints() {
super.updateViewConstraints()
tableView1HeightConstraint.constant = tableView1.contentSize.height
tableView2HeightConstraint.constant = tableView2.contentSize.height
}
Now whenever the content of any of your table views changes (e.g. when you add or remove rows or change the cell contents) you need to tell your view controller that it needs to update its constraints. Let's say you have a button that adds a cell to tableView1 each time you tap it. You might implement its action like this:
#IBAction func buttonTappen(sender: AnyObject) {
// custom method you implement somewhere else in your view controller
addRowToTableView1DataSource()
// reload table view with the updated data source
tableView1.reloadData()
// triggers an updateViewConstraints() call
view.setNeedsUpdateConstraints()
}
tl;dr:
A UITableView isn't intended for use without scrolling enabled and thus you always need to explicitly set its height when its contents change - may it be using constraints or by overriding the table view's intrinsic content size.
I have an image inside of a UIScrollView. The Image's length is much larger then the device screen size and that is why i placed it in a scrollview. However, to avoid clipping and distortion of the image I have the constraints set so the image will always have the correct aspect ratio. When you run this on different size screens, the image is different sizes. Now I want to be able to have the scrollview be the same size as my image so that the scrollview is never bigger then the image itself. Is there some code I can program in to make my scrollview always the same height as my image? I need my scrollview to change depending on the height of the image since on different devices the height of my image will change.
so far I have:
class Second : UIViewController {
#IBOutlet weak var myScrollView: UIScrollView!
#IBOutlet weak var myImage: UIImageView!
override func viewDidLoad() {
ScrollView.contentSize.height = 1300
}
The constraints I used on the image are:
Equal width as the scrollview
entered horizontally in the scrollview
Top space to the top of the scrollview
Aspect fit (so the image is not distorted)
Thank you. Any help is very appreciated!
Try this, override your 'viewDidLayoutSubviews' method and you shall be able to get the frame of the image within that method (you will not get the frame of the image in your viewDidLoad method, see reference link below). From the frame, get the height of the image, and then set the content size for the scrollview as pointed by Victor above. Let me know if it works.
See this question for reference: iOS AutoLayout - get frame size width
You can set the UIScrollView contentSize property in your viewDidLoad like the following way:
override func viewDidLoad() {
myScrollView.contentSize = CGSize(width: widthYouWantToSet, height: heightYouWantToSet)
}
But, be careful in you can experience rotations in your app, this need to be updated if the height change.
I hope this help you.
Should be,
override func viewDidLoad() {
myScrollView.contentSize.height = 1300
}
I have a non scrolling UITableView inside of a larger UIScrollView. Since the table view is non scrolling, I want its frame size to always match its content size.
I have the following method that I call:
- (void)resizeTableViewFrameHeight
{
// Table view does not scroll, so its frame height should be equal to its contentSize height
CGRect frame = self.tableView.frame;
frame.size = self.tableView.contentSize;
self.tableView.frame = frame;
}
The problem is that I must be calling this method at the wrong time (before the contentSize is set). I was wondering, is there a property that I can set on UITableView that will cause its frame to always match its content size automatically?
There is no property that dynamically adjusts table view's frame. Also, I do not think that a UITableView can calculate its entire contentSize up-front because a table view could be practically infinite in size, depending what is specified from its datasource and other delegate methods such as heightForRowAtIndexPath. These methods can be dynamic, making it virtually impossible to predict the entire size of a table view.
I conclude that you are perhaps not using this UI element correctly.
Workaround:
By having the frame of the table view constrained to the visible area of the scroll view you could simply allow it to scroll. Perhaps you need to prevent your scroll view from scrolling while the table view is scrolling and vice versa. You could easily do this in the UIScrollView delegate methods.
I needed to do something like this in iOS 8: A scroll view that contained a non-scrolling fixed-width table view with variable number of rows of content, so I wanted to dynamically resize the height of the table view.
What worked for me was to set up a height constraint on the table view with a dummy value, and then create outlets to both the table view and the constraint. Then this was all I needed:
class ViewController: UIViewController, UITableViewDataSource {
#IBOutlet weak var tableViewHeightConstraint: NSLayoutConstraint!
#IBOutlet weak var tableView: UITableView!
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
tableViewHeightConstraint.constant = tableView.contentSize.height
}
// (plus additional data source methods, etc.)
}
I can imagine situations (maybe with lots of rows, or rows of variable height) where it might not be this simple, but this worked for me.