Due to our designer being a sadist, I have a UITableView with a segmented control that switches between two different types of cells in separate feeds. The cells are dequeued with different identifiers and classes — this all works fine. The cells share a parent but are different sizes and for optimization reasons I set the layer.shadowpath manually in layoutSubviews() of the parent. I need the shadows: designer's wishes.
The issue is that after I switch to the second segment, some of the way down the table there are shadows dangling from what I believe are the cells above. As you can see from the first image, there are two shadows, and if I scroll down to occlude the top-most visible cell the shadow disappears, which leads me to the believe that the shadows are offset. Further scrolling makes these shadows disappear and not reappear again until switching tabs again. The rest of the shadows are fine.
two shadows
scroll down slightly
When switching back to the previous tab, where the cells are taller, there are also shadow issues, but those shadows are too short. As noted, the code that sets the shadow path is in the parent class, and the parent class is responsible for making and laying-out the top-most "card" view that contains the custom subCells.
I do everything programmatically: setting up the views and the Autolayout. Currently cell heights are hard-coded.
I'm not sure what information is relevant as I am completely at a loss, so here is how I set the shadowPath.
override func layoutSubviews() {
super.layoutSubviews()
cardView.layer.shadowPath = UIBezierPath(rect: cardView.bounds).CGPath
}
For simplicity the card is layout out in the contentView with the following visual format:
"V:|-marginV-[card]-marginV-|"
"H:|-marginH-[card]-marginH-|"
For whatever reason, even though I was using separate classes and separate reuseIdentifiers, the first reused cells just out of the view port were still sized as the tall cells in the other segment. When I changed
let cell = tableView.dequeueReusableCellWithIdentifier(booksReuseIdentifier) as! ProfileBookCell
to include the indexPath as
let cell = tableView.dequeueReusableCellWithIdentifier(booksReuseIdentifier, forIndexPath: indexPath) as! ProfileBookCell the recycling issue was remedied and bounds were properly computed. I tried layoutIfNeeded in a dozen different places to no effect, but this fixed it.
I had the same exact problem and I tried the current marked solution but that, nor anything else seemed to work. After trying so many other things I finally tried moving my add shadow code inside the layoutSubviews function of my subclassed UITableViewCell and it finally worked! I think this worked because the cell's final size isn't always calculated properly until the layouSubviews call and it needs the proper size to draw the shadow.
override func layoutSubviews() {
super.layoutSubviews()
addShadow(cell: self)
}
private func addShadow(cell:UITableViewCell) {
cell.layer.shadowOffset = CGSize(width:1, height:1)
cell.layer.shadowColor = UIColor.black.cgColor
cell.layer.shadowRadius = 1
cell.layer.shadowOpacity = 0.6
cell.clipsToBounds = false
let shadowFrame: CGRect = (cell.layer.bounds)
let shadowPath: CGPath = UIBezierPath(rect: shadowFrame).cgPath
cell.layer.shadowPath = shadowPath
}
Related
So I had done it several times, it worked fine, but i don't know what am I missing this time. The Problem is that image inside image view is not being clipped to bounds.
In simulator it is shown like this:
Inside the table view cell class, in my awakeFromNib() method i have following code:
override func awakeFromNib()
{
super.awakeFromNib()
profileImageView.layer.masksToBounds = true
profileImageView.clipsToBounds = true
profileImageView.layer.cornerRadius = profileImageView.frame.size.width / 2
profileImageView.layer.borderWidth = 2
profileImageView.layer.borderColor = UIColor.black.cgColor
}
but still as you see in the image, the image inside image view is coming out of the bounds, whereas circular border is correctly applied.
I have tried to put this code inside layoutSubviews() method in tableViewCell class, as well as i have also tried to apply this cornerRadius inside cellForRowAt method of table view.
Moreover, i have also tried changing the properties of image view to Aspect Fit and Scale To Fit from storyboard.
Nothing is working, The image is still shown outside the circular bounds! why ?
EDIT: Screenshot of my storyboard, where i had a prototype cell, with image view in it
As per the OP...
In cellForRowAt you're setting:
cell.imageView?.image
where you should be setting:
cell.profileImageView.image
A UITableViewCell has "built-in" .textLabel, .detailTextLabel and .imageView elements. We have to be careful not to use those objects if we've created a custom cell with our own labels / imageViews / etc.
First, I am sorry for the title, but i'm not sure what is causing my problem!
I have set up my TableViewCells developing on an iPad (ios 9) until the borders and shadows were as I wanted them... but when I run the app on an iPhone 6 (iOS 11.3) some weird changes appeared.
This is how the screen starts, and you can see the shadow irregularity in the second cell and a square background from the third cell
This is scrolling down, all cells have the shadow and border problem.
I used a UIView inside the cell to add the spacing and the style, so the View has the shadows and the corner radius, while the cell.backgroundColor = UIColor.clear.
It looks like the cell background went back to white for the cells that were totally or partially out of the screen...
Any Ideas? Thanks!
EDIT:
Thanks for the coments! I share pieces of the code below:
cell.backView.layer.borderWidth = 5
cell.backView.layer.borderColor = UIColor.clear.cgColor
cell.backView.layer.cornerRadius = 10
let shadowPath2 = UIBezierPath(rect: cell.backView.bounds)
cell.backView.layer.masksToBounds = false
cell.backView.layer.shadowColor = UIColor.black.cgColor
cell.backView.layer.shadowOffset = CGSize(width: CGFloat(1.0), height: CGFloat(3.0))
cell.backView.layer.shadowOpacity = 0.5
cell.backView.layer.shadowPath = shadowPath2.cgPath
cell.backgroundColor = UIColor.clear
So, backView is the View with all the cell's content. There are a couple of things not needed, since I was trying stuff, but as far as I know non should be the cause...
By the way, when I run the code today it looked like it should, but I didn't do anything to the tableView, I was working on a different matter completely, so the problem is not solved officially...
Also, I noticed that after coming back from a segue, sometimes, the shadows only covered the bottom half of the cell... Can that be the same problem maybe?
I think you're doing more than you need to do...
Assuming you've added your backView to your cell prototype in IB, and all the other content is contained in that view (labels, chart, etc), you can add this to your cell class:
override func awakeFromNib() {
backView.layer.cornerRadius = 10.0
backView.layer.masksToBounds = false
backView.layer.shadowColor = UIColor.black.cgColor
backView.layer.shadowOffset = CGSize(width: 1.0, height: 3.0);
backView.layer.shadowOpacity = 0.5
}
which gives this result:
No need for any other code... shadows will maintain their appearance when scrolling / rotating device / etc.
In my case problem was clipToBounds.
I unChecked it in CollectionView and CollectionViewCell and child of it.
I'm trying to set a dropshadow on a UITableView which is being added programmatically.
The frame height is being set to a certain percentage of the screen. So I want to set a dropshadow on the tableview itself.
I tried doing the following:
tableview_results.layer.shadowPath = UIBezierPath(rect: tableview_results.frame).cgPath
tableview_results.layer.shadowColor = UIColor.black.cgColor
tableview_results.layer.opacity = 1
tableview_results.layer.shadowOffset = CGSize.zero
tableview_results.layer.shadowRadius = 10
However this doesn't seem to do anything.
When I try searching for a solution, I only find stuff regarding how to set a dropshadow on the last cell of the UITableView. However this would not work for me since then the shadow will only be displayed when the last cell is displayed.
I need the shadow to be always present on the UITableView.
The shadow path should be set to the tableview_results.bounds not frame. The bounds is the rectangle around the table view in the coordinate system of the table view itself, and that's the correct coordinate system in which to specify the shadow path. Alternatively, you can just not set the shadow path at all and the shadow will draw in the correct place (though perhaps there's a performance benefit to setting the path explicitly if you can do so reliably).
You also need to set the shadowOpacity not the opacity of the layer, to 1. And you need to tell the tableview_results to not clipToBounds. So something like this:
tableview_results.layer.shadowPath = UIBezierPath(rect: tableview_results.bounds).cgPath
tableview_results.layer.shadowColor = UIColor.black.cgColor
tableview_results.layer.shadowOpacity = 1
tableview_results.layer.shadowOffset = CGSize.zero
tableview_results.layer.shadowRadius = 10
tableview_results.clipsToBounds = NO;
And for that last line, if you feel like the code is cleaner by only talking to the layer, you can equivalently use tableview_results.layer.masksToBounds = NO.
Note that there is a side effect of turning off clipping: Now you might see table view cells beyond the bounds of the table view itself! So there might be better ways to get the shadow effect. You could, for example, wrap the table view in a simple superview that tightly bounds the tableview, and give the shadow to that superview. Just a thought.
I want to accomplish a table view cell overlap since I want to accomplish this effect:
So basically cells would kind of stack one on another. I'm targeting iOS7+ (currently testing with iOS8).
I'm currently doing a CAGradientLayer which gets positioned in layoutSubviews. I'm positioning it with:
CGRect gradientLayerFrame = self.contentView.bounds;
gradientLayerFrame.origin.y = -6.0f;
gradientLayerFrame.size.height += 6.0f;
self.gradientLayer.frame = gradientLayerFrame;
And I also did the
self.clipsToBounds = NO;
self.contentView.clipsToBounds = NO;
self.contentView.superview.clipsToBounds = NO;
in init so that nothing would clip. Now the cell clips the first time it gets rendered but when I scroll the table view down and then up again so that it displays the cells again they don't get clipped.
I already tried setting backgroundColor to clear in willDisplayCell with no luck. Also tried recursively traversing all of the cells subviews and set clipping to NO but the same thing all over again. Also tried hacking and forcefully setting setNeedsDisplay so that things would re-render but no luck.
Any help on this issue would be much appreciated.
It shouldn't be a TableView.
Try this library: TGLStackedViewController
I faced the same issue some time back.
The solution that worked for me is using a Collection view instead of a TableView. Create a custom class by extending it from UICollectionViewFlowLayout and use that in your Collection View instead.
class OverlappedCustomFlowLayout: UICollectionViewFlowLayout {
override func prepare() {
super.prepare()
// This allows us to make intersection and overlapping
// A negative number implies overlapping whereas positive implies space between the adjacent edges of two cells.
minimumLineSpacing = -100
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
let layoutAttributes = super.layoutAttributesForElements(in: rect)
for currentLayoutAttributes: UICollectionViewLayoutAttributes in layoutAttributes! {
// zIndex - Specifies the item’s position on the z-axis.
// Unlike a layer's zPosition, changing zIndex allows us to change not only layer position,
// but tapping/UI interaction logic too as it moves the whole item.
currentLayoutAttributes.zIndex = currentLayoutAttributes.indexPath.row + 1
}
}
return layoutAttributes
}
P.S. - As per Apple's developer document of zIndex
This property is used to determine the front-to-back ordering of items
during layout. Items with higher index values appear on top of items
with lower values. Items with the same value have an undetermined
order. The default value of this property is 0.
Hope it helps! :)
How can I add border to UICollectionView's cells which uses a custom UICollectionViewFlowLayout? When I override UICollectionView's flow layout, cell borders are "removed". how can I set borders properly?
Thank you!
I'm not sure how you want your border to look like, but whenever I need a quick border around a view, I usually use the following:
var view = UIView(frame: frame)
view.layer.borderWidth = 1
view.layer.borderColor = UIColor.blackColor().CGColor
Update Swift 3
view.layer.borderColor = UIColor.black.cgColor
You can either probably apply this to your collection view cell or create a custom class that sets these values after initialization.
Either that, or you can set the cell size so that spaces between cells serve as borders and set minimumLineSpacing and minimumInteritemSpacing in your custom flow layout implementation.