Use UIViewController as TableView cell - ios

I have a segmented control which switches between two UIViewControllers. A info view, and a list view (TableView).
I want to use the first UIViewController as the first cell of my TableView (that is on the other segment).
Is there a way to convert a UIViewController to a cell or someway to use it as a cell for a TableView?

Use this code with your own way. Here we are adding the controllers view as subview of cell and using auto layout to manage properly. You just need to use the code by understanding.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath:indexPath)
cell.layoutIfNeeded()
let infoVC = self.storyboard.instantiateViewControllerWithIdentifier("InfoVC")
self.addChildViewController(infoVC)
cell.contentView.addSubview(infoVC.view)
infoVC.view.translatesAutoresizingMaskIntoConstraints = false
cell.contentView.addConstraint(NSLayoutConstraint(item: infoVC.view, attribute: NSLayoutAttribute.Leading, relatedBy: NSLayoutRelation.Equal, toItem: cell.contentView, attribute: NSLayoutAttribute.Leading, multiplier: 1.0, constant: 0.0))
cell.contentView.addConstraint(NSLayoutConstraint(item: infoVC.view, attribute: NSLayoutAttribute.Trailing, relatedBy: NSLayoutRelation.Equal, toItem: cell.contentView, attribute: NSLayoutAttribute.Trailing, multiplier: 1.0, constant: 0.0))
cell.contentView.addConstraint(NSLayoutConstraint(item: infoVC.view, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: cell.contentView, attribute: NSLayoutAttribute.Top, multiplier: 1.0, constant: 0.0))
cell.contentView.addConstraint(NSLayoutConstraint(item: infoVC.view, attribute: NSLayoutAttribute.Bottom, relatedBy: NSLayoutRelation.Equal, toItem: cell.contentView, attribute: NSLayoutAttribute.Bottom, multiplier: 1.0, constant: 0.0))
infoVC.didMoveToParentViewController(self)
infoVC.view.layoutIfNeeded()
}

Swift 5.0 Syntax
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for:indexPath)
cell.layoutIfNeeded()
let storyboard = UIStoryboard(name: "StoryboardName", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "ViewControllerNameHere")
self.addChild(vc)
cell.contentView.addSubview(vc.view)
vc.view.translatesAutoresizingMaskIntoConstraints = false
cell.contentView.addConstraint(NSLayoutConstraint(item: vc.view, attribute: NSLayoutConstraint.Attribute.leading, relatedBy: NSLayoutConstraint.Relation.equal, toItem: cell.contentView, attribute: NSLayoutConstraint.Attribute.leading, multiplier: 1.0, constant: 0.0))
cell.contentView.addConstraint(NSLayoutConstraint(item: vc.view, attribute: NSLayoutConstraint.Attribute.trailing, relatedBy: NSLayoutConstraint.Relation.equal, toItem: cell.contentView, attribute: NSLayoutConstraint.Attribute.trailing, multiplier: 1.0, constant: 0.0))
cell.contentView.addConstraint(NSLayoutConstraint(item: vc.view, attribute: NSLayoutConstraint.Attribute.top, relatedBy: NSLayoutConstraint.Relation.equal, toItem: cell.contentView, attribute: NSLayoutConstraint.Attribute.top, multiplier: 1.0, constant: 0.0))
cell.contentView.addConstraint(NSLayoutConstraint(item: vc.view, attribute: NSLayoutConstraint.Attribute.bottom, relatedBy: NSLayoutConstraint.Relation.equal, toItem: cell.contentView, attribute: NSLayoutConstraint.Attribute.bottom, multiplier: 1.0, constant: 0.0))
vc.didMove(toParent: self)
vc.view.layoutIfNeeded()
return cell

Well, i'd recomend to create a custom UITableViewCell with its own nib file and load it from nib.
This can than added/dequeud to a UITableViewSection.
The Cell itself can then be design inside of the UI Builder in any way you want it to look like. You can put literaly everything into the cell you like.
Here is a short tutorial how todo so:
using-nib-xib-files
A question to that topic can be find here:
Custom UITableViewCell from nib in Swift

Related

Reload tableView section with beginUpdates

I am reloading from a UIButton with observers an UITableView on my Swift App.
I'm using this code:
self.myTableView.beginUpdates()
self.myTableView.reloadSections(NSIndexSet(index: 1), withRowAnimation: UITableViewRowAnimation.None)
self.myTableView.endUpdates()
self.myTableView.layoutIfNeeded()
It's working perfectly on iOS 13, but on iOS 12 I need push the UIButton two times to reload correctly an UITableViewCell that contains a multiline UILabel and the headerView.
How can I do or what need I change on my code?
My content of Cell is:
let sectionView: UIView = SectionView.fromNib()//Nib haven't nothing added. All from this code
var constraints = [NSLayoutConstraint]()
let titleLabel = UILabel()
titleLabel.text = "Hello World"
titleLabel.sizeToFit()
titleLabel.translatesAutoresizingMaskIntoConstraints = false
sectionView.addSubview(titleLabel)
constraints.append(NSLayoutConstraint(item: titleLabel, attribute: NSLayoutConstraint.Attribute.leading, relatedBy: NSLayoutConstraint.Relation.equal, toItem: sectionView, attribute: NSLayoutConstraint.Attribute.leading, multiplier: 1.0, constant: 10.0))
constraints.append(NSLayoutConstraint(item: titleLabel, attribute: NSLayoutConstraint.Attribute.trailing, relatedBy: NSLayoutConstraint.Relation.equal, toItem: sectionView, attribute: NSLayoutConstraint.Attribute.trailing, multiplier: 1.0, constant: -10.0))
constraints.append(NSLayoutConstraint(item: titleLabel, attribute: NSLayoutConstraint.Attribute.top, relatedBy: NSLayoutConstraint.Relation.equal, toItem: sectionView, attribute: NSLayoutConstraint.Attribute.top, multiplier: 1.0, constant: 10.0))
constraints.append(NSLayoutConstraint(item: titleLabel, attribute: NSLayoutConstraint.Attribute.bottom, relatedBy: NSLayoutConstraint.Relation.equal, toItem: sectionView, attribute: NSLayoutConstraint.Attribute.bottom, multiplier: 1.0, constant: -10.0))
sectionView.addConstraints(constraints)
EDIT: I have also tried performBatchUpdates then rs7 comment but I'm getting the same problem, the row with an UILabel not reload without scroll on iOS 12...
self.myTableView.performBatchUpdates ({
self.myTableView.reloadSections(NSIndexSet(index: 1), withRowAnimation: UITableViewRowAnimation.None)
},
completion: { (success) in
self.myTableView.layoutIfNeeded()
}
)
Thanks!

Adding a uiview on a uiview in uitableviewcell

I am passing a view to UITableViewCell from cellForForAtIndex method. I want cell to resize based upon the content of UIView but cell's height is not changing. I have tried to add constraint from coding when I add custom view.
UITableViewCell method:
func addContainerView(view:UIView){
vwContainer.addSubview(view)
view.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint(item: view, attribute: .leading, relatedBy: .equal, toItem: vwContainer, attribute: .leading, multiplier: 1.0, constant: 0.0).isActive = true
NSLayoutConstraint(item: view, attribute: .trailing, relatedBy: .equal, toItem: vwContainer, attribute: .trailing, multiplier: 1.0, constant: 0.0).isActive = true
NSLayoutConstraint(item: view, attribute: .top, relatedBy: .equal, toItem: vwContainer, attribute: .top, multiplier: 1.0, constant: 0.0).isActive = true
NSLayoutConstraint(item: view, attribute: .bottom, relatedBy: .equal, toItem: vwContainer, attribute: .bottom, multiplier: 1.0, constant: 0.0).isActive = true
}
Above code is called from the following code in CellForRowAtIndex:
if let rowObj = objData?.itemsArray?[indexPath.row] as? TableViewDataFormat, let cell = tableView.dequeueReusableCell(withIdentifier: TableViewCell, for: indexPath) as? HiltiTableViewCell {
if let container = rowObj.containerview{
cell.addContainerView(view: container)
cell.lblTitle.text = rowObj.cellTitle
}
return cell
}
The objective here is to create a custom uitableview in a custom framework where I can pass a UIView and cell should resize based upon the content of the view passed in.
Edit 1:
Tried to add content over contentview like following:
cell.contentView.addSubview(container)
but still getting an overlapped view as follows:
It should have bee 3 different cells, first one with uiview and other 2 with image inside

Broken constraints in the cell

I need to show some UIView in my cell, but size of this UIView (backView) is different for each cell. So I need to change the height of this cell also.
I'm trying to change height and width of this backView in runtime, but all that I'm getting is a bunch of Autolayout errors like that:
[LayoutConstraints] Unable to simultaneously satisfy constraints. Will attempt to recover by breaking constraint...
I created SimpleTextCell class for this cell, and its init looks like that:
self.backView = UIView()
self.backView.translatesAutoresizingMaskIntoConstraints = false
self.backView.backgroundColor = UIColor.yellow
self.backView.layer.cornerRadius = 8.0
self.contentView.addSubview(backView)
let constraintBackViewTop = NSLayoutConstraint(item: backView, attribute: .top, relatedBy: .equal, toItem: contentView, attribute: .topMargin, multiplier: 1.0, constant: -5)
let constraintBackViewBottom = NSLayoutConstraint(item: backView, attribute: .bottom, relatedBy: .equal, toItem: contentView, attribute: .bottomMargin, multiplier: 1.0, constant: 5)
let constraintBackViewLeft = NSLayoutConstraint(item: backView, attribute: .left, relatedBy: .equal, toItem: contentView, attribute: .leftMargin, multiplier: 1.0, constant: 0)
constraintBackHeight = NSLayoutConstraint(item: backView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: superHeight)
constraintBackWidth = NSLayoutConstraint(item: backView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: superWidth)
self.contentView.addConstraints([constraintBackViewTop, constraintBackViewBottom, constraintBackViewLeft, constraintBackHeight, constraintBackWidth])
superHeight and superWidth are just some default values here.
my cellForRowAt function is like that:
let cell = tableView.dequeueReusableCell(withIdentifier: "simpleTextCell", for: indexPath) as! SimpleTextCell
cell.selectionStyle = .none
cell.backgroundColor = UIColor.red
cell.constraintBackHeight.constant = 100
cell.constraintBackWidth.constant = 200
cell.updateConstraints()
And it doesn't work at all. I'm having errors (like that I've mentioned earlier), and although the height of my cell is kinda right, my backView is invisible because width of backView.frame is zero (I don't have a clue why) and the height of backView is 100 (the same). It seems that my new constraints are ignored.
I've tried to set priority for these constraints in cellForRowAt:
cell.constraintBackHeight.priority = UILayoutPriority.init(rawValue: 999)
The result is run-time error.
I've tried to overcome this error by something like this:
cell.constraintBackHeight.isActive = false
cell.constraintBackHeight.priority = UILayoutPriority.init(rawValue: 999)
cell.constraintBackHeight.isActive = true
but it doesn't work either.
How can I fix this?
The problem is here
constraintBackHeight = NSLayoutConstraint(item: backView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: superHeight)
constraintBackWidth = NSLayoutConstraint(item: backView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: superWidth)
you're setting 2 height constraints , it may be you just copied to make as a width and forget to change it to .width

ios swift - auto layout programatically to subview

I'm adding a prompt to purchase the full version of the app by displaying a uiview to the user; however, I'm having issues settings constraints programatically as to not have the issue shown in the image below on the iPhone 6 plus.
I'm doing:
let purchasePopUp = PromptPurchase.instanceFromNib() as! PromptPurchase
//purchasePopUp.frame = UIScreen.mainScreen().bounds
purchasePopUp.translatesAutoresizingMaskIntoConstraints = false
// let leadingConstraint = NSLayoutConstraint(item: purchasePopUp, attribute: .Leading, relatedBy: .Equal, toItem: view, attribute: .Leading, multiplier: 1, constant: 0)
let trailingConstraint = NSLayoutConstraint(item: purchasePopUp, attribute: .Trailing, relatedBy: .Equal, toItem: view, attribute: .Trailing, multiplier: 1, constant: 0)
// let topConstraint = NSLayoutConstraint(item: purchasePopUp, attribute: .Top, relatedBy: .Equal, toItem: view, attribute: .Top, multiplier: 1, constant: 0)
let bottomConstraint = NSLayoutConstraint(item: purchasePopUp, attribute: .Bottom, relatedBy: .Equal, toItem: view, attribute: .Bottom, multiplier: 1, constant: 0)
//view.addConstraints([leadingConstraint, trailingConstraint, topConstraint, bottomConstraint])
view.addConstraints([trailingConstraint, bottomConstraint])
view.addSubview(purchasePopUp)
And it's still only taking up the same space.
Thank you
With EasyPeasy your code would look like this, give it a try :)
import EasyPeasy
if let purchasePopUp = PromptPurchase.instanceFromNib() as? PromptPurchase {
view.addSubview(purchasePopUp)
purchasePopUp <- Edges()
}
https://github.com/nakiostudio/EasyPeasy

Swift - Add NSLayoutConstraint to my Button

I want to have a button always in bottom and in left and right corner.
I want to do this with adding constraints to my button programatically.
My Code:
class LoginController: UIViewController {
#IBOutlet weak var LoginButton: UIButton!
override func loadView() {
super.loadView()
//Button Height Constraint
let constraintButtonPlayWidth = NSLayoutConstraint (item: self.LoginButton,
attribute: NSLayoutAttribute.Height,
relatedBy: NSLayoutRelation.Equal,
toItem: nil,
attribute: NSLayoutAttribute.NotAnAttribute,
multiplier: 1,
constant: 80)
self.view.addConstraint(constraintButtonPlayWidth)
//Button Right Constraint
let r = NSLayoutConstraint(item: self.LoginButton, attribute: .Right,
relatedBy: .Equal, toItem: self.view, attribute: .Right, multiplier: 1.0, constant: 0.0)
//Button Left Constraint
let l = NSLayoutConstraint(item: self.LoginButton, attribute: .Left,
relatedBy: .Equal, toItem: self.view, attribute: .Left, multiplier: 1.0, constant: 100.0)
//Button Bottom Constraint
let b = NSLayoutConstraint(item: self.LoginButton, attribute: .Bottom,
relatedBy: .Equal, toItem: self.view, attribute: .Bottom, multiplier: 1.0, constant: 100.0)
self.view.addConstraints([l,b,r])
When I run this code:
When i add Constraints in Designer:
and when i run the App it works
my Question: What is my mistake?
If you have a view in a storyboard with no constraints, the Interface Builder will generate some constraints automatically. These auto generated constraints will conflict with the code generated ones.
So, in my opinion you have four choices.
Generate Button in Code:
Create an Constraint in Interface Builder and remove them at build time by selecting constraint in Interface Builder and check "Remove at build time"
Remove auto generated Constraints in Code
Create Constraints in Interface Builder
Option 1 - Generate Button in Code
override func viewDidLoad()
{
super.viewDidLoad()
let button = UIButton()
button.setTitle("Login", forState: UIControlState.Normal)
button.backgroundColor = UIColor.redColor()
button.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(button)
let heightConstraint = NSLayoutConstraint(
item: button,
attribute: NSLayoutAttribute.Height,
relatedBy: NSLayoutRelation.Equal,
toItem: nil,
attribute: NSLayoutAttribute.NotAnAttribute,
multiplier: 1.0,
constant: 80
)
button.addConstraint(heightConstraint)
let leftConstraint = NSLayoutConstraint(
item: button,
attribute: NSLayoutAttribute.Leading,
relatedBy: NSLayoutRelation.Equal,
toItem: self.view,
attribute: NSLayoutAttribute.Leading,
multiplier: 1.0,
constant: 0
)
self.view.addConstraint(leftConstraint)
let rightConstraint = NSLayoutConstraint(
item: button,
attribute: NSLayoutAttribute.Trailing,
relatedBy: NSLayoutRelation.Equal,
toItem: self.view,
attribute: NSLayoutAttribute.Trailing,
multiplier: 1.0,
constant: 0
)
self.view.addConstraint(rightConstraint)
let topConstraint = NSLayoutConstraint(
item: button,
attribute: NSLayoutAttribute.Bottom,
relatedBy: NSLayoutRelation.Equal,
toItem: self.view,
attribute: NSLayoutAttribute.Bottom,
multiplier: 1,
constant: 0
)
self.view.addConstraint(topConstraint)
}
Option 2 - Constraints in Code (with Constraint in IB removed at build time)
Create at least one constraint (e.g. a height constraint on the login button) and set the Remove at build time checkbox. After that you will get storyboard errors, you can fix them by adding more constraints (but i think it is not really necessary to fix the storyboard errors)
override func viewDidLoad()
{
super.viewDidLoad()
self.LoginButton.translatesAutoresizingMaskIntoConstraints = false
let heightConstraint = NSLayoutConstraint(
item: self.LoginButton,
attribute: NSLayoutAttribute.Height,
relatedBy: NSLayoutRelation.Equal,
toItem: nil,
attribute: NSLayoutAttribute.NotAnAttribute,
multiplier: 1.0,
constant: 80
)
self.LoginButton.addConstraint(heightConstraint)
let leftConstraint = NSLayoutConstraint(
item: self.LoginButton,
attribute: NSLayoutAttribute.Leading,
relatedBy: NSLayoutRelation.Equal,
toItem: self.view,
attribute: NSLayoutAttribute.Leading,
multiplier: 1.0,
constant: 0
)
self.view.addConstraint(leftConstraint)
let rightConstraint = NSLayoutConstraint(
item: self.LoginButton,
attribute: NSLayoutAttribute.Trailing,
relatedBy: NSLayoutRelation.Equal,
toItem: self.view,
attribute: NSLayoutAttribute.Trailing,
multiplier: 1.0,
constant: 0
)
self.view.addConstraint(rightConstraint)
let topConstraint = NSLayoutConstraint(
item: self.LoginButton,
attribute: NSLayoutAttribute.Bottom,
relatedBy: NSLayoutRelation.Equal,
toItem: self.view,
attribute: NSLayoutAttribute.Bottom,
multiplier: 1,
constant: 0
)
self.view.addConstraint(topConstraint)
}
Option 3 - Remove auto generated Constraints in Code
override func viewDidLoad()
{
super.viewDidLoad()
var removeConstraints : [NSLayoutConstraint] = []
for constraint in self.view.constraints
{
if constraint.firstItem === self.LoginButton
{
removeConstraints.append(constraint)
}
}
self.view.removeConstraints(removeConstraints)
self.LoginButton.removeConstraints(self.LoginButton.constraints)
self.LoginButton.translatesAutoresizingMaskIntoConstraints = false
let heightConstraint = NSLayoutConstraint(
item: self.LoginButton,
attribute: NSLayoutAttribute.Height,
relatedBy: NSLayoutRelation.Equal,
toItem: nil,
attribute: NSLayoutAttribute.NotAnAttribute,
multiplier: 1.0,
constant: 80
)
self.LoginButton.addConstraint(heightConstraint)
let leftConstraint = NSLayoutConstraint(
item: self.LoginButton,
attribute: NSLayoutAttribute.Leading,
relatedBy: NSLayoutRelation.Equal,
toItem: self.view,
attribute: NSLayoutAttribute.Leading,
multiplier: 1.0,
constant: 0
)
self.view.addConstraint(leftConstraint)
let rightConstraint = NSLayoutConstraint(
item: self.LoginButton,
attribute: NSLayoutAttribute.Trailing,
relatedBy: NSLayoutRelation.Equal,
toItem: self.view,
attribute: NSLayoutAttribute.Trailing,
multiplier: 1.0,
constant: 0
)
self.view.addConstraint(rightConstraint)
let topConstraint = NSLayoutConstraint(
item: self.LoginButton,
attribute: NSLayoutAttribute.Bottom,
relatedBy: NSLayoutRelation.Equal,
toItem: self.view,
attribute: NSLayoutAttribute.Bottom,
multiplier: 1,
constant: 0
)
self.view.addConstraint(topConstraint)
}
Option 4 - Create in Interface Builder
Create Constraints in Interface Builder
If you want the button at the bottom, where you are setting the constraint, remove the 100 constant.
let b = NSLayoutConstraint(item: self.button, attribute: .Bottom,
relatedBy: .Equal, toItem: self.view, attribute: .Bottom, multiplier: 1.0, constant: 0.0)
If you are targeting iOS 9 you can also add your constraints like this:
button.addConstraint(button.heightAnchor.constraintEqualToConstant(80))
self.view.addConstraint(self.view.leadingAnchor.constraintEqualToAnchor(button.leadingAnchor))
self.view.addConstraint(self.view.trailingAnchor.constraintEqualToAnchor(button.trailingAnchor))
self.view.addConstraint(self.view.bottomAnchor.constraintEqualToAnchor(button.bottomAnchor))

Resources