UITableViewAutomaticDimension with programmatically added images - ios

I have a problem with UITableViewAutomaticDimension:
In my tableview I have multiple imageviews created programmatically with calculated height:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "managePlayset", for: indexPath) as! ManagePlaysetTableViewCell
let playset = playsets[indexPath.row]
cell.playsetName.text = playset.name
if (playset.musicItems?.count)! > 0 {
var i = 0
for musicItem in (playset.musicItems?.array)! {
let imageView = UIImageView(image: (musicItem as! MusicItem).getFirstPhoto())
imageView.frame = CGRect(x: cell.contentView.frame.origin.x+CGFloat(i)*(imageWidth+10.0), y: cell.contentView.frame.origin.y+40, width: imageWidth, height: imageWidth)
imageView.contentMode = .scaleAspectFit
cell.contentView.addSubview(imageView)
let constraint = NSLayoutConstraint(item: imageView,
attribute: NSLayoutAttribute.bottomMargin, relatedBy:
NSLayoutRelation.equal, toItem: cell.contentView,
attribute: NSLayoutAttribute.bottomMargin, multiplier: 1,
constant: 0)
cell.contentView.addConstraint(constraint)
i = i + 1
}
}
cell.editButton.tag = indexPath.row
return cell
}
In viewDidLoad I set:
let width = view.frame.width
imageWidth = width / CGFloat(maxItemsOnPage) - 10.0
tableView.estimatedRowHeight = imageWidth + 20.0
tableView.rowHeight = UITableViewAutomaticDimension
But height is not adjusted properly (images are cut off)
Any advice is highly appreciated!

I think it's because UIImageView created programmatically.
Then it don't use autolayout system.
To change it, you have to add
imageView.translatesAutoresizingMaskIntoConstraints = false in your loop.

Have you tried constraining both top and bottom edges of the imageView? i.e.
let constraintTop = NSLayoutConstraint(item: imageView,
attribute: NSLayoutAttribute.top, relatedBy:
NSLayoutRelation.equal, toItem: cell.contentView,
attribute: NSLayoutAttribute.topMargin, multiplier: 1,
constant: 0)
let constraintBottom = NSLayoutConstraint(item: imageView,
attribute: NSLayoutAttribute.bottom, relatedBy:
NSLayoutRelation.equal, toItem: cell.contentView,
attribute: NSLayoutAttribute.bottomMargin, multiplier: 1,
constant: 0)
cell.contentView.addConstraints([constraintTop, constraintBottom])

Related

Shadow rate on UITableViewCell contenview increasing while scrolling

In my app, I'm using dynamic height cells using auto layout. So for creating cardview effect I've to use tableview willdisplaycell method. It only add shadow only once to cell. But I don't know why shadow increasing while scrolling.
Here's my code
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
//setup card view style on cell
if !cellArray.contains(indexPath.row) {
cell.contentView.backgroundColor = UIColor.clear
let whiteRoundedView : UIView = UIView(frame: CGRect(x: 5.0, y: 5.0, width: cell.contentView.frame.size.width-10, height: cell.contentView.frame.size.height-10))
whiteRoundedView.layer.backgroundColor = UIColor.white.cgColor
whiteRoundedView.layer.masksToBounds = false
whiteRoundedView.layer.cornerRadius = 5.0
whiteRoundedView.layer.shadowOffset = CGSize(width: -1, height: 1)
whiteRoundedView.layer.shadowOpacity = 0.5
cell.contentView.addSubview(whiteRoundedView)
cell.contentView.sendSubview(toBack: whiteRoundedView)
cellArray.add(indexPath.row)
}
}
this piece of line is the causing the issue.
cell.contentView.addSubview(whiteRoundedView)
whenever willDisplayCell gets called, you will be adding the view everytime. That is the reason shadow is getting increased while scrolling. The solution is,
1. check the cell content view if the view is already added or not. If not, then add the view. use view tag to do it.
2. otherwise, create a shadow view and initialize the specific things in the -(void)awakeFromNib, which will get called only once.
but personally, i prefer option 2, which will isolate your view render logic from view controller or cell.
add the below code to your custom cell class. In this case, i have given leading-trialing-top-bottom constraints as 15-15-15-15. If you wish you can set it to 0. but make sure that it should in sync with the background constraints.
override func awakeFromNib() {
super.awakeFromNib()
contentView.backgroundColor = UIColor.clear
let whiteRoundedView : UIView = UIView(frame: CGRect(x: 5.0, y: 5.0, width: contentView.frame.size.width-10, height: contentView.frame.size.height-10))
whiteRoundedView.layer.backgroundColor = UIColor.white.cgColor
whiteRoundedView.layer.masksToBounds = false
whiteRoundedView.layer.cornerRadius = 5.0
whiteRoundedView.layer.shadowOffset = CGSize(width: -1, height: 1)
whiteRoundedView.layer.shadowOpacity = 0.5
whiteRoundedView.tag = shadowTag
whiteRoundedView.translatesAutoresizingMaskIntoConstraints=false
contentView.addSubview(whiteRoundedView)
contentView.sendSubview(toBack: whiteRoundedView)
let leading = NSLayoutConstraint(item: whiteRoundedView,
attribute: .leading,
relatedBy: .equal,
toItem: contentView,
attribute: .leading,
multiplier: 1.0,
constant: 15.0)
let trailing = NSLayoutConstraint(item: whiteRoundedView,
attribute: .trailing,
relatedBy: .equal,
toItem: contentView,
attribute: .trailing,
multiplier: 1.0,
constant: -15.0)
let top = NSLayoutConstraint(item: whiteRoundedView,
attribute: .top,
relatedBy: .equal,
toItem: contentView,
attribute: .top,
multiplier: 1.0,
constant: 15.0)
let bottom = NSLayoutConstraint(item: whiteRoundedView,
attribute: .bottom,
relatedBy: .equal,
toItem: contentView,
attribute: .bottom,
multiplier: 1.0,
constant: -15.0)
contentView.addConstraint(leading)
contentView.addConstraint(trailing)
contentView.addConstraint(top)
contentView.addConstraint(bottom)
}
Ref screenshot :
You can add your code here
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: <idenitfier>, for: indexPath) as? JobListTableViewCell
if cell == nil {
//Initialization and setting of shadow
}
return cell
}
Hope this helps

Add UIImageView, UILabel to UIStackView programmatically in Swift 4

I'm trying to create a set of UIImageView, UILabel and add to UIStackView programmatically like so.
var someLetters: [Int: String] = [0:"g",1:"n",2:"d"]
let stackView1 = UIStackView(arrangedSubviews: createLetters(someLetters))
someScrollView.addSubview(stackView1)
func createLetters(_ named: [Int: String]) -> [UIView] {
return named.map { name in
let letterImage = UIImageView()
letterImage.image = UIImage(named: "\(name.key)")
let letterLabel = UILabel()
letterLabel.text = name.value
let subView = UIView()
subView.addSubview(letterLabel)
subView.addSubview(letterImage)
return subView
}
}
UIStackViews arrangedSubviews only accepts UIView as a parameter
so I created an additional UIView as a container of UILabel and UIImageView. No compile error but not seeing any UI elements on a screen.
What am I doing wrong here?
Add some constraints to
stackView1.alignment = .center
stackView1.distribution = .fillEqually
stackView1.axis = .vertical
stackView1.spacing = 10.0
stackView1.translatesAutoresizingMaskIntoConstraints = false
let leading = NSLayoutConstraint(item: stackView1, attribute: .leading, relatedBy: .equal, toItem: someScrollView, attribute: .leading, multiplier: 1, constant: 5)
let trailing = NSLayoutConstraint(item: stackView1, attribute: .trailing, relatedBy: .equal, toItem: someScrollView, attribute: .trailing, multiplier: 1, constant: 5)
let height = NSLayoutConstraint(item: stackView1, attribute: .height, relatedBy: .equal, toItem: someScrollView, attribute: .height, multiplier: 0.8, constant: 50)
let alignInCenter = NSLayoutConstraint(item: stackView1, attribute: .centerX, relatedBy: .equal, toItem: someScrollView, attribute: .centerX, multiplier: 1, constant: 1)
let alignInCenterY = NSLayoutConstraint(item: stackView1, attribute: .centerY, relatedBy: .equal, toItem: someScrollView, attribute: .centerY, multiplier: 1, constant: 1)
someScrollView.addSubview(stackView1)
NSLayoutConstraint.activate([alignInCenter,alignInCenterY,leading, trailing, height])
and also add some constraints to letters and ImageView
func createLetters(_ named: [Int: String]) -> [UIView] {
return named.map { name in
let letterImage = UIImageView()
letterImage.image = UIImage(named: "\(name.key)")
letterImage.backgroundColor = UIColor.gray
let letterLabel = UILabel()
letterLabel.text = name.value
letterLabel.backgroundColor = UIColor.green
let stackView = UIStackView(arrangedSubviews: [letterLabel, letterImage])
stackView.alignment = .center
stackView.distribution = .fillEqually
stackView.axis = .horizontal
let widht = NSLayoutConstraint(item: letterImage, attribute: NSLayoutAttribute.width, relatedBy: .equal, toItem: stackView, attribute: .width, multiplier: 1, constant: 100)
let height = NSLayoutConstraint(item: letterImage, attribute: NSLayoutAttribute.height, relatedBy: .equal, toItem: stackView, attribute: .height, multiplier: 1, constant: 100)
NSLayoutConstraint.activate([widht, height])
return stackView
}
}
But I am not sure what axis and what kind of distribution you are looking for in stackView. This code has some of the constraints missing so you have to identify and add them so there is no ambiguity in layouts
As a starting point, try adding the UILabels and UIImageViews to stack views in a storyboard. Here you can see I'm using horizontal stack views contained in a vertical stack view. From here you can see the constraints you'll need to use to create the same in code.
Update code as:
func createLetters(_ named: [Int: String]) -> [UIView] {
return named.map { name in
let letterImage = UIImageView(frame: CGRect(x: 0, y: name.key*10, width: 20, height: 20))
letterImage.image = UIImage(named: "(name.key)")
letterImage.backgroundColor = .green
let letterLabel = UILabel(frame: CGRect(x: 30, y: name.key*10, width: 20, height: 20))
letterLabel.text = name.value
letterLabel.backgroundColor = .red
let stackView = UIStackView(frame: CGRect(x: 0, y:name.key*30, width: 100, height: 50))
stackView.axis = UILayoutConstraintAxis.vertical
stackView.distribution = UIStackViewDistribution.equalSpacing
stackView.alignment = UIStackViewAlignment.center
stackView.spacing = 16.0
stackView.addArrangedSubview(letterImage)
stackView.addArrangedSubview(letterLabel)
return stackView
}
}
This will return 3 stack views as someLetters array count is 3. call it as
self.tview.addSubview(createLetters(someLetters)[0])
self.tview.addSubview(createLetters(someLetters)[1])
self.tview.addSubview(createLetters(someLetters)[2])
if you to get only one stack view label and image with same code then pass only one parameter.

AutoLayout issue UITable View header section: Adding image in the left does not works fine

I have added a custom image to my UITableView header section. It's working fine when the screen is in portrait mode. Large gap appears in landscape mode. Can any one help me out?
override func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
var headerView = UIView(frame: CGRect(x: 1, y: 1, width: tableView.frame.width, height: 40))
var myLabel = UILabel()
myLabel.frame = CGRectMake(0, 0, tableView.frame.width - 70, 40)
print(myLabel.frame)
myLabel.font = UIFont.boldSystemFontOfSize(18)
myLabel.backgroundColor = UIColor.blueColor()
myLabel.text = self.tableView(tableView, titleForHeaderInSection: section)
let button = UIButton(frame: CGRect(x: 230,y: 0,width: 100,height: 40))
button.tag = section
button.backgroundColor = UIColor.clearColor()
headerView.addSubview(button)
headerView.addSubview(myLabel)
headerView.backgroundColor = UIColor.clearColor()
// the button is image - set image
button.setImage(UIImage(named: "icoDraft"), forState: UIControlState.Normal)
let tapOnCardCell: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(HHLabTestExaminationViewController.handleTapOnSectionImage(_:)))
button.addGestureRecognizer(tapOnCardCell)
return headerView
}
Now the title for header sections are
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if(section == 0)
{
return "Exam"
}
else if(section == 1)
{
return "News"
}
else if(section == 2)
{
return "Movie"
}
else if(section == 3)
{
return "Sport"
}
return ""
}
This image is my output when the screen is in portrait orientation:
This is when the screen is in landscape orientation. How can i fix this gap in landscape?
I don't see any autolayout code when you set up this header view. However, this simple layout can be handled without autolayout.
headerView.autoResizesSubviews = true
myLabel.autoResizingMask = [.flexibleWidth, .flexibleHeight]
button.autoResizingMask = .flexibleLeftMargin
As #DaveWeston says, there is no auto layout code, and his answer should work fine. If you want auto layout, here is what it would look like for the button.
(Note this is Swift 3 vs. 2.x in the OP's code).
let button = UIButton()
button.tag = section
button.backgroundColor = UIColor.clear
headerView.addSubview(button)
// Autolayout for button
button.translatesAutoresizingMaskIntoConstraints = false
button.addConstraint(NSLayoutConstraint.init(item: button,
attribute: .height,
relatedBy: .equal,
toItem: headerView,
attribute: .height,
multiplier: 1.0,
constant: 0.0))
button.addConstraint(NSLayoutConstraint.init(item: button,
attribute: .width,
relatedBy: .equal,
toItem: nil,
attribute: .width,
multiplier: 1.0,
constant: 40.0))
button.addConstraint(NSLayoutConstraint.init(item: button,
attribute: .trailing,
relatedBy: .equal,
toItem: headerView,
attribute: .trailing,
multiplier: 1.0,
constant: 0.0))
button.addConstraint(NSLayoutConstraint.init(item: button,
attribute: .centerY,
relatedBy: .equal,
toItem: headerView,
attribute: .centerY,
multiplier: 1.0,
constant: 0.0))

Can't add constraints between UICollectionView cell and the cell subview

After I run my app, it crashes:
Unable to install constraint on view. Does the constraint reference something from outside the subtree of the view? That's illegal.
My image is subview of the cell.
What am I doing wrong?
let image = UIImageView(frame: CGRect(x: 0, y: 0, width: 300, height: 50))
image.image = UIImage.init(named: "Bitmap")
cell.contentView.addSubview(image)
let bottomConstr = NSLayoutConstraint(item: image, attribute: .bottom, relatedBy: .equal, toItem: cell.contentView, attribute: .bottom, multiplier: 1, constant: 0)
//let leftConstr = NSLayoutConstraint(item: image, attribute: .leading, relatedBy: .equal, toItem: cell.contentView, attribute: .leading, multiplier: 1, constant: 0)
//let rightConstr = NSLayoutConstraint(item: image, attribute: .trailing, relatedBy: .equal, toItem: cell.contentView, attribute: .trailing, multiplier: 1, constant: 0)
let heightConstr = NSLayoutConstraint(item: image, attribute: .height, relatedBy: .equal, toItem: nil , attribute: .notAnAttribute, multiplier: 1, constant: 10)
image.addConstraint(bottomConstr)
//image.addConstraint(leftConstr)
//image.addConstraint(rightConstr)
image.addConstraint(heightConstr)
You need to add constraint on Superview. so, add it on cell contentView.
cell.contentView.addConstraint(bottomConstr)
cell.contentView.addConstraint(heightConstr)
Constraints weren't working well for me when zooming UICollectionView. Here's how I resized a label to always fill the cell.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "mycell", for: indexPath as IndexPath) as! SliceCollectionViewCell
cell.label.autoresizingMask = [.flexibleWidth, .flexibleHeight]
}
func collectionView(_ collectionView: UICollectionView,
willDisplay cell: UICollectionViewCell,
forItemAt indexPath: IndexPath) {
guard let cell = cell as? MyCollectionViewCell else {
print("ERROR: wrong cell type")
return
}
cell.label.frame = CGRect(x: 0, y: 0, width: cell.frame.width, height: cell.frame.height)
}

Place UILabel center of parent

I am trying to put UILable at the center of each cell of UICollectionView.
Here is my code:
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as! CollectionViewCell
// Configure the cell
cell.backgroundColor = UIColor.brownColor()
let title = UILabel(frame: CGRectMake(cell.bounds.midX, cell.bounds.midY, cell.bounds.size.width, 20)) //
title.text = "ddd"
title.textColor = UIColor.whiteColor()
// title.center = cell.center
cell.contentView.addSubview(title)
// let xCenterConstraint = NSLayoutConstraint(item: title, attribute: .CenterX, relatedBy: .Equal, toItem: cell, attribute: .CenterX, multiplier: 1, constant: 0)
// let yCenterConstraint = NSLayoutConstraint(item: title, attribute: .CenterY, relatedBy: .Equal, toItem: cell, attribute: .CenterY, multiplier: 1, constant: 0)
// let leadingConstraint1 = NSLayoutConstraint(item: title, attribute: .Leading, relatedBy: .Equal, toItem: cell, attribute: .Leading, multiplier: 1, constant: 20)
// let trailingConstraint1 = NSLayoutConstraint(item: title, attribute: .Trailing, relatedBy: .Equal, toItem: cell, attribute: .Trailing, multiplier: 1, constant: -20)
// cell.addConstraints([xCenterConstraint, yCenterConstraint, leadingConstraint1, trailingConstraint1])
// title.centerXAnchor.constraintEqualToAnchor(cell.centerXAnchor).active = true
// title.centerYAnchor.constraintEqualToAnchor(cell.centerYAnchor).active = true
return cell
}
The output:
Seems I need to change my code, so what am I missing?
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as! CollectionViewCell
// Configure the cell
cell.backgroundColor = UIColor.brownColor()
//Editing Starts
let title = UILabel(frame: CGRectMake(cell.bounds.midX -(cell.bounds.size.width/2) , cell.bounds.midY - 10, cell.bounds.size.width, 20))
//Editing Ends
//
title.text = "ddd"
title.textColor = UIColor.whiteColor()
// title.center = cell.center
cell.contentView.addSubview(title)
// let xCenterConstraint = NSLayoutConstraint(item: title, attribute: .CenterX, relatedBy: .Equal, toItem: cell, attribute: .CenterX, multiplier: 1, constant: 0)
// let yCenterConstraint = NSLayoutConstraint(item: title, attribute: .CenterY, relatedBy: .Equal, toItem: cell, attribute: .CenterY, multiplier: 1, constant: 0)
// let leadingConstraint1 = NSLayoutConstraint(item: title, attribute: .Leading, relatedBy: .Equal, toItem: cell, attribute: .Leading, multiplier: 1, constant: 20)
// let trailingConstraint1 = NSLayoutConstraint(item: title, attribute: .Trailing, relatedBy: .Equal, toItem: cell, attribute: .Trailing, multiplier: 1, constant: -20)
// cell.addConstraints([xCenterConstraint, yCenterConstraint, leadingConstraint1, trailingConstraint1])
// title.centerXAnchor.constraintEqualToAnchor(cell.centerXAnchor).active = true
// title.centerYAnchor.constraintEqualToAnchor(cell.centerYAnchor).active = true
return cell
}
Try following code it may helps you
let title = UILabel(frame: CGRectMake(0, 0, cell.bounds.size.width, 20))
title.textAlignment=.Center
title.text = "ddd"
title.textColor = UIColor.whiteColor()
title.center = cell.contentView.center
cell.contentView.addSubview(title)

Resources