Can't change UITableViewCell's subview's position - ios

So I have a static tableview in place and I'd like to change the position of the label inside the first tableviewcell. I have IBOutlet for the first cell.
This is how I try to change the label's position:
UILabel *label1 = [tempTC.cell1.contentView.subviews firstObject];
label1.frame = CGRectMake(10, 10, 10, 10);
The problem is that the frame doesn't change, however I can do everything else to the label like change it's text and size etc, but changing the frame doesn't seem to work.
Is there something I am missing?

When using auto layout, you should set frames, you should change the constraints instead. The easiest way to do this is to make IBOutlets to the ones you need to change, and change the constant value of the constraint. For instance, if you had a constraint to the left side called leftCon, you could do something like this:
self.leftCon.constant = 30;

When you want to change frame subview those were constraint in XIB. You need to set translatesAutoresizingMaskIntoConstraints = YES.
label1.translatesAutoresizingMaskIntoConstraints = YES;
label1.frame = CGRectMake(10, 10, 10, 10);
For detail:
Can I use setFrame and autolayout on the same view?

First of all according to comments, disable auto layout.
Second if You want refer to textLabel use [[UITableViewCell textLabel] setFrame:CGRectMake(10,10,10,10)];
BTW. If You'r first UITablViewCell is special I suggest use .xib and subclass UITableviewCell. Apply that subclass to prototype UITableViewCell in storyboard, then add this cell protype cell as property/global variable.
If you need more description please ask :).

Make a custom UITableViewCell.
Look at the example below.
Inside tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) method I am dynamically changing TimeStatus (UIView) frame size:
.
.
.
let cell:OneTVChannelTableViewCell = tableView.dequeueReusableCellWithIdentifier(OneCurrentChannelTCIdentifier, forIndexPath: indexPath) as OneTVChannelTableViewCell
.
.
.
cell.setForRepairDynamicViewWidth(channelTimeStatusWidth)
.
.
.
And here is my simple custom UITableViewCell:
class OneTVChannelTableViewCell: UITableViewCell {
#IBOutlet weak var Number: UILabel!
#IBOutlet weak var Image: UIImageView!
#IBOutlet weak var Title: UILabel!
#IBOutlet weak var TimeStatus: UIView!
#IBOutlet weak var TimeBack: UIView!
var RepairWidth:CGFloat = 0.0
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
override func layoutSubviews() {
super.layoutSubviews()
TimeStatus.frame.size.width = RepairWidth
}
func setForRepairDynamicViewWidth(width:CGFloat)
{
RepairWidth = width
}
}

Related

Programatically laying out subviews in a stackView

I would like a UITableCell to contain a variable number of buttons, for example "Yes" and "No" buttons, or "Red", "Green" and "Blue" buttons. I've tried various approached but settled on using a vertical stackView within the cell and programmatically adding a view containing the button (feel free to suggest a better approach here).
The issue I'm having is that I can't figure out what auto layout is doing. Normally, when you add views to a stackView in interface builder, you just need a height value and then they stack nicely. However the following code appears to give them a width of zero and if I set the width, the views are all on top of each other. Note this doesn't include the buttons I've mentioned above to make responding easier; I've just added two views.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "formQuestion") as! FormQuestionTableViewCell
let view = UIView(frame: .zero)
view.backgroundColor = UIColor.red
view.translatesAutoresizingMaskIntoConstraints = false
cell.questionOptions.addSubview(view)
NSLayoutConstraint.activate([
view.heightAnchor.constraint(equalToConstant: 8)
// , view.widthAnchor.constraint(equalToConstant: 16)
])
let view2 = UIView(frame: .zero)
view2.backgroundColor = UIColor.blue
view2.translatesAutoresizingMaskIntoConstraints = false
cell.questionOptions.addSubview(view2)
NSLayoutConstraint.activate([
view2.heightAnchor.constraint(equalToConstant: 32)
// , view2.widthAnchor.constraint(equalToConstant: 64)
])
return cell
}
And for the sake of completeness, the FormQuestionTableViewCell looks like this...
class FormQuestionTableViewCell: UITableViewCell {
#IBOutlet weak var descriptionContainer: UIView!
#IBOutlet weak var controlContainer: UIView!
#IBOutlet weak var descriptionLabel: UILabel!
#IBOutlet weak var questionOptions: UIStackView!
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
}
}
This looks like this when the width constraints are enabled (though I believe this shouldn't be necessary)...
Any help you can give me would be very much appreciated!
This is wrong:
cell.questionOptions.addSubview(view)
When adding a subview to UIStackView, you should use:
.addArrangedSubview()
Reference:
https://developer.apple.com/documentation/uikit/uistackview/1616227-addarrangedsubview
You are saying
cell.questionOptions.addSubview(view)
That's wrong. questionOptions is a stack view. If you want it to take responsibility for laying out view, you must say
cell.questionOptions.addArrangedSubview(view)

autoResizingMask for inputAccessoryView not working

I'm trying to cope with iPhone X with inputAccessoryView. I've added a view to my ViewController like so:
CustomView
I've defined an outlet and attached to it. Returned the same view as inputAccessoryView like so:
class ViewController: UIViewController {
#IBOutlet weak var textFieldContainer: UIView!
override var inputAccessoryView: UIView? {
return textFieldContainer
}
override var canBecomeFirstResponder: Bool {
return true
}
override func viewDidLoad() {
super.viewDidLoad()
textFieldContainer.autoresizesSubviews = true
}
}
I've made sure to add constraints relative to safe area. Here are my constraints:
Constraints
Adjusted autoResizingMask in SB like so:
AutoResizingMask
However, it's still not working. Here's the output:
Output
What did I miss?
Unlike for table view cells, there is no support for dynamic height calculation in input accessory views, as far as I know.
You could use a fixed height for the accessory view.
But I assume, that you want just to change either top, bottom, or height constraint in interface builder and the change is reflected after the next build.
What you could do is, to use a custom view class where you connect your top, bottom and height constraints.
Then override intrinsicContentSize and return the sum of the three constraint constants.
class TextFieldContainer: UIView {
#IBOutlet weak var topConstraint: NSLayoutConstraint!
#IBOutlet weak var bottomConstraint: NSLayoutConstraint!
#IBOutlet weak var heightConstraint: NSLayoutConstraint!
override var intrinsicContentSize: CGSize {
let contentHeight =
self.topConstraint.constant
+ self.heightConstraint.constant
+ self.bottomConstraint.constant
return CGSize(width: UIScreen.main.bounds.width, height: contentHeight)
}
}
Your layout hierarchy could be simplified and look like this:
The autoresizing mask could look like this:
The final result would behave like the following then:
You're giving textfield height and also bottom constraint, try to remove bottom constraint for textfield

Change Swift constraint on if statement

Here is my tableview row/cell:
there are constraints set in place - the imageview is below the label and the button is below the imageview.
here is my code:
if(row == 1) {
imageview.hidden = false
} else {
imageview.hidden = true
//how can i change the button constraint from below imageview to below label?
Adding and removing constraints is really bad example for that. I'll make your UI more complex.
Best way of solving these auto-layout problems is adding two constraints. One from imageView to button and second from imageView to label.
Now after setting these constraints, you need to set their priority levels. So, let's say button will be below the imageView first. In this case, you need to set imageView to button constraint's priority to something like 750 or UILayoutPriorityDefaultHigh and label to button constraint's priority to 250 or UILayoutPriorityDefaultLow.
Let's start creating a custom UITableViewCell
class YourTableViewCell: UITableViewCell {
#IBOutlet weak var imageView: UIImageView!
#IBOutlet weak var label: UILabel!
#IBOutlet weak var button: UIButton!
#IBOutlet weak var buttonToLabelConstraint: NSLayoutConstraint!
#IBOutlet weak var buttonToImageViewConstraint: NSLayoutConstraint!
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
func shouldHideImageView(hidden: Bool) {
if(hidden == false) {
buttonToLabelConstraint.priority = UILayoutPriorityDefaultLow
buttonToImageViewConstraint.priority = UILayoutPriorityDefaultHigh
imageView.hidden = true
} else {
buttonToLabelConstraint.priority = UILayoutPriorityDefaultHigh
buttonToImageViewConstraint.priority = UILayoutPriorityDefaultLow
imageView.hidden = false
}
self.contentView.layoutIfNeeded()
}
}
After that, in your class where tableView is placed implement a logic like that:
if(row == 1) {
cell.shouldHideImageView(true)
} else {
cell.shouldHideImageView(false)
}
You should be all set.
You can try using a StackView, when you tell something to be hidden, the imageView the stack view will adjust the StackView as if the imageView was never a part of the view and it is an easy work around to not have to worry about constraints.
You can create IBOutlet on constraint and then just simply change the value like this:
buttonConstraint.constant = newValue
But i suggest you create for this a tableView. In this case you code and logic, i think, will be more accurate.
you could to this instead of hiding.
Make an outlet from the heights constraint of the imageview, call it constraint for now.
Set constraint.constant = 0 // effectively same as hiding.
Set constraint.constant = NON_ZERO_VALUE // effectively same as show.
hope it helps!
I see a couple of options. The first is a little easier to implement but a little less flexible if you decide to change your layout later.
Make the button's constraint to be below the label. Keep a reference to this constraint (you can connect it to your code via storyboard just like you do with the button itself, if you're using storyboard). When the imageView is visible, set myConstraint.constant += myImageView.frame.height. When the imageView is hidden, set myConstraint.constant -= myImageView.frame.height. Afterwards, call view.setNeedsLayout to update your constraints.
Make two constraints: one for below the image, and one for below the label ("constraintToImage" and "constraintToLabel"). Hook them both up to your controller like in option 1, and call view.addConstraint(constraintToImage) and view.removeConstraint(constraintToLabel) when the image becomes visible (and the opposite for when it's hidden). Again, call view.setNeedsLayout after.

Resize xib in custom UICollectionViewCell

I have a custom UICollectionViewCell that is laid out in an .xib file. The size is 194 x 200 AND auto layout constraints are set:
and this is what the class looks like
class DurationDayCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var banner: UIView!
#IBOutlet weak var numberOfHours: UILabel!
#IBOutlet weak var unitHrs: UILabel!
#IBOutlet weak var dayOfWeek: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
}
But in my collection view it looks like this:
The size of the cells are smaller (97 x 100). IS there a way to make the nib to auto-adjust to the size of the collection view cell?
Had the exact same problem, solved with either of those two solutions:
in your storyboard, click Collection View, click size inspector, Set "Estimate Size" to "None"
or
in your custom cell class, add this method:
override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
let preferredLayoutAttributes = layoutAttributes
var adjustedFrame = layoutAttributes.frame
adjustedFrame.size.height = 180 //change according to your case
adjustedFrame.size.width = 100
preferredLayoutAttributes.frame = adjustedFrame
return preferredLayoutAttributes
}
Click your xib on stotyboard and find right side Simulator Metrics and change size to -> Freeform later click Show size inspector select freeform and write there your width and height later build will works !
Thanks

Swift ScrollView In UITableViewCell not scrolling

I am new to swift and am trying to add a UIScrollview to a custom UITableViewCell. I can't seem to get the scrollview to scroll in the simulator though. I have tested both vertical and horizontal scrolling.
Here is the code for my custom UITableViewCell
import UIKit
class CustomTableViewCell: UITableViewCell, UIScrollViewDelegate {
#IBOutlet weak var winLoseValueLabel: UILabel!
#IBOutlet weak var dateLabel: UILabel!
#IBOutlet weak var newTotalLabel: UILabel!
#IBOutlet weak var locationLabel: UILabel!
#IBOutlet weak var playersLabel: UILabel!
#IBOutlet weak var playersScrollView: UIScrollView!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
self.playersLabel.text = nil
//self.playersScrollView.contentSize.height = 1000
self.playersScrollView.contentSize.width = 1000
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
func addLabelToScrollView(str : String) {
if self.playersLabel.text == nil{
self.playersLabel.text = str
}
else{
self.playersLabel?.text = self.playersLabel.text! + "\n\(str)"
}
}
}
My playersScrollView outlet has been properly connected to the scrollview in my storyboard. As far as I can see from all of the tutorials and sources online, I should just have to set the contentSize width or height (larger than the view area) to get it to scroll, which I have done in the awakeFromNib function.
Any ideas why this is not working in the simulator? Secondly, ideally I would like to have a vertical scrolling scrollview in the table cell, but I thought that the table view's vertical scrolling might have been interfering with the scrollview, so I changed it for horizontal scrolling for testing, which also didn't work.
Can you have a vertical scrollview in a vertically scrolling UITableView, or will the table view scrolling interfere too much with the scrollview?
Ok, it turns out that the constraints on my playersLabel, which is a subview of playersScrollView, was the culprit, although I am not sure why. I adjusted the constraints on the label and that allowed the horizontal scrolling to work.
The vertical scrolling still did not work, so I had to override the touchesBegan and touchesEnded methods in the scroll view so I could disable/re-enable the scrolling of the table view.

Resources