Draw a cell of UITableView give a visual error - ios

I write a code to draw card in a UITableView, each card need to draw itself and have a custom rowheight in the exemple a draw 2 card in 240 height and 8 other with 240/2 of height.
I give you a screen of the problem :
The last but one card have a strange design. When I print the bounds of the frame of it, this show me the same size that the other ...
I give you my code where I draw the card :
func drawBasiqCard() -> CGFloat{
if(cardView.subviews.count == 0){
self.addSubview(cardView)
cardView.frame = CGRect(marginCardWidth,
marginCardHeight,
self.bounds.size.width - (marginCardWidth*2),
basiqCardtHeight - (marginCardHeight*2))
cardView.layer.cornerRadius = 10
print(cardView.bounds.size.height)
}
if(self.ShadowLayerCard == nil){
let rounding = CGFloat.init(10)
let shadowLayer = CAShapeLayer.init()
self.ShadowLayerCard = shadowLayer
shadowLayer.path = UIBezierPath.init(roundedRect: cardView.bounds, cornerRadius: rounding).cgPath
shadowLayer.fillColor = UIColor(rgb: 0xffcc00).cgColor
shadowLayer.shadowPath = shadowLayer.path
shadowLayer.shadowColor = UIColor.black.cgColor
shadowLayer.shadowRadius = 5
shadowLayer.shadowOpacity = 0.2
shadowLayer.shadowOffset = CGSize.init(width: 0, height: 0)
cardView.layer.insertSublayer(shadowLayer, at: 0)
}
return cardView.bounds.size.height + (marginCardHeight*2)
}
So my question is, the code above is good ? Or my error are not here ? Where is the code can be produce this visual error ?
If you need the code of the UITableView I can show you.
Thank for your reply.

This could happen if you set the shadow of the view before it's bounds has been set.
Your current design is not good, you should add a shadow to the contentView in init and then change the contentView's frame(add some padding) in layoutsubviews.
Here is an example:
import UIKit
class TableViewCell: UITableViewCell {
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
contentView.do {
$0.backgroundColor = .white
$0.addShadow(ofColor: .black, radius: 1.5, offset: .zero, opacity: 0.1)
$0.cornerRadius = 6
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
contentView.pin.all(UIEdgeInsets(inset: 5))
}
override func setHighlighted(_ highlighted: Bool, animated: Bool) {
super.setHighlighted(highlighted, animated: animated)
contentView.backgroundColor = highlighted ? UIColor(rgb: 217) : .white
}
}
It looks like:

Related

UIImageView circular shape swift

I used this code to make UIImageView in a custom UITableViewCell, but the circular shape isn't updated until i pressed on the table cell, I tried the code in both awakeFromNib() and layoutSubviews()
userImage.layer.cornerRadius = userImage.frame.size.width / 2
userImage.layer.masksToBounds = true
userImage.layer.borderColor = UIColor.black.cgColor
userImage.layer.borderWidth = 1
You simply have to override the layoutSubviews() method of your UITableViewCell subclass, as it will be called on drawing updates.
Then just set your UIImageView's cornerRadius as you like:
class MyCustomTableViewCell: UITableViewCell {
private let circularImageView: UIImageView = {
$0.contentMode = .scaleAspectFill
$0.layer.masksToBounds = true
$0.layer.borderColor = UIColor.black.cgColor
$0.layer.borderWidth = 1
return $0
}(UIImageView())
// ...
override func layoutSubviews() {
super.layoutSubviews()
circularImageView.layer.cornerRadius = circularImageView.frame.size.width/2
}
}
if you create you tableviewCell programmatically you have to override init
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
//put your code here
}
update the imageViews cornerRadius just after a little time. Because frame.size.width / 2 will be different at runtime :
DispatchQueue.main.asyncAfter(deadline : .now() + 0.05 {
self.userImage.layer.cornerRadius = userImage.frame.size.width / 2
self.userImage.layer.masksToBounds = true
self.userImage.layer.borderColor = UIColor.black.cgColor
self.userImage.layer.borderWidth = 1
}

UICollectionViewCell - Round Downloaded Image

I am trying to set a collectionView with round images I get using KingFisher (image caching library).
I am not sure what I should set with round corner, the imageView or the cell itself. Even if the cell is round the image seems to fill the square.
So far I am using this code (If I dont set it after I change the image its square):
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
...
if let path = user.profilePictureURL {
if let url = URL(string: path) {
cell.profilePictureImageView.kf.setImage(with: url)
cell.profilePictureImageView.layer.cornerRadius = cell.profilePictureImageView.frame.width/2.0
}
}
And the ImageView :
class ProfilePicture : UIImageView {
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
self.commonInit()
}
override init(frame: CGRect) {
super.init(frame: frame)
self.commonInit()
}
func commonInit(){
self.layer.masksToBounds = false
self.layer.cornerRadius = self.layer.frame.width/2.0
self.clipsToBounds = true
}
}
But the first time it downloads the images , they are like this (cell background is blue)
You can use like this:
extension UIImageView {
func setRounded() {
let radius = CGRectGetWidth(self.frame) / 2
self.layer.cornerRadius = radius
self.layer.masksToBounds = true
}
}
then call the method as below:
imageView.setRounded()
You can get a circle by adjusting height (in portrait) instead of width:
self.layer.cornerRadius = self.layer.frame.height/2.0
you'll have to adjust your contentMode.scaleAspect if you are unhappy with the results. By adjusting on the long edge you can get a circle but you won't see the entire image.
You can get a circle in the image with a extension of UIView
extension UIImage {
var circleMask: UIImage {
let square = size.width < size.height ? CGSize(width: size.width, height: size.width) : CGSize(width: size.height, height: size.height)
let imageView = UIImageView(frame: CGRect(origin: CGPoint(x: 0, y: 0), size: square))
imageView.contentMode = UIView.ContentMode.scaleAspectFill
imageView.image = self
imageView.layer.cornerRadius = square.width/2
//imageView.layer.borderColor = UIColor.white.cgColor
//imageView.layer.borderWidth = 5
imageView.layer.masksToBounds = true
UIGraphicsBeginImageContext(imageView.bounds.size)
imageView.layer.render(in: UIGraphicsGetCurrentContext()!)
let result = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return result!
}}

Adding horizontal line below cell in uicollectionview

I am implementing like a calendar layout with some modification which is shown in the screenshot. To achieve this I have used UICollectionView. The problem is, I have to draw a screen width continuous line(green line in screenshot). The green line should cover the whole width, I know its not showing over the circular cell due to half of the cornerRadius and a vertical line only after the first cell(10 am). Where i have to add the shapelayer, so that it ll seems like a continuous line. Here is the code which I have tried so far.
KKViewController.m
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as! KKBookCollectionViewCell
self.bookingCollectionView.backgroundColor = UIColor.whiteColor()
let rectangularRowIndex:NSInteger = indexPath.row % 5
if(rectangularRowIndex == 0 )
{
cell.userInteractionEnabled = false
cell.layer.cornerRadius = 0
cell.timeSlotLabel.text = "10am"
cell.backgroundColor = UIColor.whiteColor()
cell.layer.borderWidth = 0
cell.layer.borderColor = UIColor.clearColor().CGColor
}
else
{
cell.userInteractionEnabled = true
cell.layer.cornerRadius = cell.frame.size.width/2
cell.timeSlotLabel.text = ""
//cell.backgroundColor = UIColor.lightGrayColor()
cell.layer.borderWidth = 1
cell.layer.borderColor = UIColor.grayColor().CGColor
if cell.selected == true
{
cell.backgroundColor = UIColor.greenColor()
}
else
{
cell.backgroundColor = UIColor.lightGrayColor()
}
}
return cell
}
KKCollectionCell.m
var borderWidth:CGFloat!
var borderPath:UIBezierPath!
override func awakeFromNib() {
super.awakeFromNib()
drawHorizontalLine()
dottedLine(with: borderPath, and: borderWidth)
drawVerticalLine()
dottedLine(with: borderPath, and: borderWidth)
func drawVerticalLine()
{
borderPath = UIBezierPath()
borderPath.moveToPoint(CGPointMake(self.frame.origin.x + self.frame.size.width, self.frame.origin.y))
//borderPath.addLineToPoint(CGPointMake(self.frame.size.width - 5, self.frame.origin.y + self.frame.size.height - 50))
borderPath.addLineToPoint(CGPointMake(self.frame.origin.x + self.frame.size.width, self.frame.origin.y + self.frame.size.height))
borderWidth = 2.0
print("border path is %f, %f:\(borderPath)")
}
func drawHorizontalLine()
{
borderPath = UIBezierPath()
borderPath.moveToPoint(CGPointMake(0, self.frame.origin.y))
borderPath.addLineToPoint(CGPointMake(self.frame.size.width + 10, self.frame.origin.y))
borderWidth = 2.0
print("border path is %f, %f:\(borderPath)")
}
func dottedLine (with path:UIBezierPath, and borderWidth:CGFloat)
{
let shapeLayer = CAShapeLayer()
shapeLayer.strokeStart = 0.0
shapeLayer.strokeColor = UIColor.greenColor().CGColor
shapeLayer.lineWidth = borderWidth
shapeLayer.lineJoin = kCALineJoinRound
shapeLayer.lineDashPattern = [1,2]
shapeLayer.path = path.CGPath
self.layer.addSublayer(shapeLayer)
}
You can add a new view inside the collection view cell and set the corner radius for that new view. Also you have to reduce the spacing between the cells. Then the line will look like what you expected.
I know it's an old question but it was never properly answered, i was looking for such behaivior and achieved it with the help of another answer.
To achieve that you need to subclass the UICollectionViewFlowLayout (maybe a simple layout also would do but i didn't try).
class HorizontalLineFlowLayout: UICollectionViewFlowLayout {
var insets: CGFloat = 10.0
static let lineWidth: CGFloat = 10.0
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
guard let attributes = super.layoutAttributesForElements(in: rect) else { return nil }
var attributesCopy: [UICollectionViewLayoutAttributes] = []
for attribute in attributes {
attributesCopy += [attribute]
let indexPath = attribute.indexPath
if collectionView!.numberOfItems(inSection: indexPath.section) == 0 { continue }
let contains = attributes.contains { layoutAttribute in
layoutAttribute.indexPath == indexPath && layoutAttribute.representedElementKind == HorizontalLineDecorationView.kind
}
if !contains {
let horizontalAttribute = UICollectionViewLayoutAttributes(forDecorationViewOfKind: HorizontalLineDecorationView.kind, with: indexPath)
let width = indexPath.item == collectionView!.numberOfItems(inSection: indexPath.section) - 1 ?
attribute.frame.width + insets :
attribute.frame.width + insets * 1.5
let frame = CGRect(x: attribute.frame.minX, y: attribute.frame.minY, width: width, height: 0.5)
horizontalAttribute.frame = frame
attributesCopy += [horizontalAttribute]
}
}
return attributesCopy
}
override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
return true
}
the func layoutAttributesForElements is called each time, and it makes the lines for the cells you specify and for the frames you specify.
you would also need the decoration view which looks like that:
class HorizontalLineDecorationView: UICollectionReusableView {
static let kind = "HorizontalLineDecorationView"
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .gray
alpha = 0.2
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
in general its just a view that goes behind the cell, respectively to its indexPath, so the lines are not all along the screen but those are some lines gathered togather which look like a full line, you can adjust its width and height, play with that.
notice that frame that is set for an attribute in the layout, that is the frame of the decoration view, and is defined with respect to the cell (attribute).
dont forget to register that decoration view and also to make the layout and pass it to the collecitonview like this:
let layout = HorizontalLineFlowLayout()
layout.register(HorizontalLineDecorationView.self, forDecorationViewOfKind: HorizontalLineDecorationView.kind)
layout.minimumInteritemSpacing = 10.0
layout.minimumLineSpacing = 10.0
layout.sectionInset = .zero
collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
collectionView.register(DateCell.self, forCellWithReuseIdentifier: "month")
collectionView.delegate = monthDelegate
collectionView.dataSource = monthDelegate
collectionView.backgroundColor = .clear
the end result is

Accessory Type in UITableViewCell subclass is not visible

I'm doing everything programmatically. I have a UITableView with its rows being a UITableViewCell subclass. I have been unable to set the accessoryType of my cell as nothing appears on the screen. Later on I want to be able to select a cell, and add a checkmark when selected.
class ContactsTableViewCell: UITableViewCell {
let name = UILabel()
let phoneNumber = UILabel()
let iPADFACTOR:CGFloat = DeviceHelper().isIPAD ? 1.4: 1.0
var rowSelected = false
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
phoneNumber.font = UIFont.systemFontOfSize(15)
phoneNumber.textColor = UIColor.lightGrayColor()
self.contentView.addSubview(name)
self.contentView.addSubview(phoneNumber)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
let margeV:CGFloat = 5.0*iPADFACTOR
let margeH = self.frame.width*0.05
name.frame = CGRectMake(margeH, margeV, self.frame.width - margeH * 4, 18)
phoneNumber.frame = CGRectMake(margeH, name.frame.maxY + 5, self.frame.width - margeH * 4, 15)
}
}
As you can see I'm using the layoutSubviews() method, to set the frame of my labels and I've tried playing with the label size to check if somehow it was hiding the accessoryView without success. I've even tried setting their frame to 0.
I'm also not sure if I'm setting my frames correctly. Should I use self.contentView.frame instead of self.frame ?
Any help is appreciated, thanks.
override func layoutSubviews() {
super.layoutSubviews()
let margeV:CGFloat = 5.0*iPADFACTOR
let margeH = self.frame.width*0.05
name.frame = CGRectMake(margeH, margeV, self.frame.width - margeH * 4, 18)
phoneNumber.frame = CGRectMake(margeH, name.frame.maxY + 5, self.frame.width - margeH * 4, 15)
}
if (YES) {
cell.accessoryType = UITableViewCellAccessoryType.Checkmark
} else {
cell.accessoryType = UITableViewCellAccessoryType.None;
}

Custom Table View Cell Drawing Wrong Height When Scrolling

I'm customizing my table view cell inside the willDisplayCell method. For some reason it is drawing some subviews with the wrong height while scrolling (see video). I can't figure out why...
override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
cell.contentView.backgroundColor=UIColor.clearColor()
cell.reloadInputViews()
let height = cell.frame.height - 15
var whiteRoundedCornerView:UIView!
whiteRoundedCornerView=UIView(frame: CGRectMake(7,10,self.view.bounds.width-14,height))
whiteRoundedCornerView.backgroundColor=getColorByID(colorID!)
whiteRoundedCornerView.layer.masksToBounds=false
whiteRoundedCornerView.layer.shadowOpacity = 1.55;
whiteRoundedCornerView.layer.shadowOffset = CGSizeMake(1, 0);
whiteRoundedCornerView.layer.shadowColor=UIColor.grayColor().CGColor
whiteRoundedCornerView.layer.cornerRadius=5.0
whiteRoundedCornerView.layer.shadowOffset=CGSizeMake(-1, -1)
whiteRoundedCornerView.layer.shadowOpacity=0.5
whiteRoundedCornerView.layer.shouldRasterize = true
whiteRoundedCornerView.layer.rasterizationScale = UIScreen.mainScreen().scale
if cell.contentView.subviews.count < 6 { // to avoid multible subview adding when scrolling...
cell.contentView.addSubview(whiteRoundedCornerView)
cell.contentView.sendSubviewToBack(whiteRoundedCornerView)
}
}
Here is a video of the issue:
https://vid.me/QeV0
Update:
I also tried to move the code to layoutSubviews but still the same issue...
override func layoutSubviews() {
super.layoutSubviews()
let height = self.frame.height - 15
var whiteRoundedCornerView:UIView!
whiteRoundedCornerView=UIView(frame: CGRectMake(7,10,UIScreen.mainScreen().bounds.width-14,height))
whiteRoundedCornerView.backgroundColor=UIColor.greenColor()//getColorByID(colorID!)
whiteRoundedCornerView.layer.masksToBounds=false
whiteRoundedCornerView.layer.shadowOpacity = 1.55;
whiteRoundedCornerView.layer.shadowOffset = CGSizeMake(1, 0);
whiteRoundedCornerView.layer.shadowColor=UIColor.grayColor().CGColor
whiteRoundedCornerView.layer.cornerRadius=5.0
whiteRoundedCornerView.layer.shadowOffset=CGSizeMake(-1, -1)
whiteRoundedCornerView.layer.shadowOpacity=0.5
whiteRoundedCornerView.layer.shouldRasterize = true
whiteRoundedCornerView.layer.rasterizationScale = UIScreen.mainScreen().scale
if self.contentView.subviews.count < 6 {
self.contentView.addSubview(whiteRoundedCornerView)
self.contentView.sendSubviewToBack(whiteRoundedCornerView)
}
}
If i don't check the tableview.subviews.count it will add tons of subviews to the cell while scrolling.
I want to change the height of the existing subview when the cell is reused.
You have a check to not add the subviews multiple times. When that case happens you already have added the subviews earlier but their height may now be wrong and you must update them.
UITableView reuses cells so a new cell with different height might already contain your subviews but with the wrong height since you don't update it.
You should cleanly separate the set-up of your subview and the layout like so:
class MyCell: UITableViewCell {
private let whiteRoundedCornerView = UIView()
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setUp()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setUp()
}
override func layoutSubviews() {
super.layoutSubviews()
let bounds = self.bounds
var whiteRoundedCornerViewFrame = CGRect()
whiteRoundedCornerViewFrame.origin.x = 7
whiteRoundedCornerViewFrame.origin.y = 10
whiteRoundedCornerViewFrame.size.width = bounds.width - 7 - whiteRoundedCornerViewFrame.origin.x
whiteRoundedCornerViewFrame.size.height = bounds.height - 5 - whiteRoundedCornerViewFrame.origin.y
whiteRoundedCornerView.frame = whiteRoundedCornerViewFrame
}
private func setUp() {
setUpWhiteRoundedCornerView()
}
private func setUpWhiteRoundedCornerView() {
let child = whiteRoundedCornerView
child.backgroundColor = .greenColor()
let layer = child.layer
layer.cornerRadius = 5.0
layer.rasterizationScale = UIScreen.mainScreen().scale
layer.shadowColor = UIColor.grayColor().CGColor
layer.shadowOffset = CGSize(width: -1, height: -1)
layer.shadowOpacity = 0.5
layer.shouldRasterize = true
insertSubview(child, atIndex: 0)
}
}
If the data on different rows is of different size then you have to set the height of the tableview cell dynamically
Here is the code:
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat{
var height:CGFloat
let constraint1:CGSize = CGSizeMake(self.view.frame.size.width/2-70, 5000.0)
let string="eefdgvfgfhgfgfhgfgjhidffjo;dkfnkjfldjfkjfkjfldfjkkjhbkjflfjihkfjkfjgfkjfkgfkgjlfgnfjgnfgnfgbkgmfjgfkgjfkggjlfgklkgkgkglkgggfkglgkkgjlgkgk"
let answerSize = string.boundingRectWithSize(constraint1, options: NSStringDrawingOptions.UsesLineFragmentOrigin, attributes: [NSFontAttributeName: UIFont.systemFontOfSize(17.0)], context: nil)
let questionSize = string.boundingRectWithSize(constraint1, options: NSStringDrawingOptions.UsesLineFragmentOrigin, attributes: [NSFontAttributeName: UIFont.systemFontOfSize(17.0)], context: nil)
height=answerSize.height + questionSize.height
return height
}

Resources