Adding an overlay view to UICollectionViewCell - keeps overlaying - ios

I'm trying to add a 50% black alpha view on every collection view cell. The collection view cells have a background photograph and text on top. Would like the overlay view to be in between the two.
In my cellForItemAt method, I use the following:
let overlayView = UIView()
overlayView.backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.5)
overlayView.frame = cell.bounds
cell.addSubview(overlayView)
The problem is as i scroll down, then up, the overlay keeps adding, making the alpha much darker than 50%. Additionally, the overlayView is being added on top of my text (I need it below the text.)
How can I prevent the overlay from adding multiple times, and adding it in between the correct layers of the cell?

UITableView has a method of reusing cells to make it more efficient (keeping only the required cells in memory). Because of this the reused cell may already have this subview, so calling addSubview again causes another view to be added on top of it.
Here is how to solve this:
Move addSubview(overlayView) to the layoutSubviews() method inside your cell subclass.
override func layoutSubviews() {
super.layoutSubviews()
addSubview(overlayView)
}
Remove the overlay view in the prepareForReuse() method inside your cell subclass.
override func prepareForReuse() {
super.prepareForReuse()
overlayView.removeFromSuperview()
}
Note that this requires you to define the overlay view in the cell's subclass (which you should probably do since the cell itself should be responsible for its own subviews).

This happens because your cells are dequeued and reused multiple times, therefore, you are adding multiple layers.
Put this code inside your cell's class
override func awakeFromNib() {
super.awakeFromNib()
let overlayView = UIView()
overlayView.backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.5)
overlayView.frame = self.bounds
self.addSubview(overlayView)
}
Or if you want to achieve the same result you can set black backgroundColor and set imageView's alpha to 50%
cell.backgroundColor = .black
cell.imageView.alpha = 0.5

Avoid adding this in cellForItem. The cells are reused, hence if you keep adding the view it would add one top of another. On reusing the cell the previously added view is not removed. Instead you can add the view in Prototype cell or XIB and set its alpha to whatever you want. Or if you are creating the cell programatically you can it in awakeFromNib()

Related

Swift UITableViewCell Shadow not appearing

I am trying to create custom table view cell which works fine in my other UIViewControllers. However, in one of my controllers, the shadow is not growing, I can barely see the shadow.
Here is an image of the shadow being shown in red, you can see it is barely visible.
My cell has a UIView added inside the contentView to creating floating cell effects - the same code and same storyboard layouts are being used across my controllers but this is the only table view where the shadow issue is occurring - so I must be missing something.
My addShadow extension:
extension UIView {
func addShadow(offset: CGSize, color: UIColor, radius: CGFloat, opacity: Float) {
layer.masksToBounds = false
layer.shadowOffset = offset
layer.shadowColor = color.cgColor
layer.shadowRadius = radius
layer.shadowOpacity = opacity
}
}
My awakeFromNib on the custom cell:
:: cellContentView is my UIView added to the base contentView of the cell.
override func awakeFromNib() {
super.awakeFromNib()
self.backgroundColor = .clear
self.selectionStyle = .none
cellContentView?.layer.masksToBounds = true
cellContentView?.round(corners: [.topLeft, .topRight, .bottomLeft, .bottomRight], radius: 10)
cellContentView?.addShadow(offset: CGSize(width: 40, height: 60), color: UIColor.red, radius: 10, opacity: 1)
cellContentView?.layer.shouldRasterize = true
}
Note: The .round is an extension being used on all my cells.
No matter what radius or offset I add for this shadow, it does not get bigger than the image. Also, none of my other cells in the their controllers require the shouldRasterize property to be set, but this does.
Does anyone know what is happening here?
Thanks :)
Edit
Strangely, if I add constraints around my view to keep the gaps large between my view and the cell content view, the background colour disappears - this is set to white in the storyboard.
You should call in the layoutSubviews method. because shadow should add after the view is uploaded
override func awakeFromNib() {
super.awakeFromNib()
//init methods
}
override public func layoutSubviews() {
super.layoutSubviews()
//Added shadow
self.reloadLayers()
}
private func reloadLayers() {
self.layer.cornerRadius = 5
self.addShadow(.TransactionCell)
}
I hope it helps
Content view will fill you cell, so you need to add shadow to view inside content view which has all your components inside it. Then add constraints to it with gap between that view and content view. Second, 40 and 60 properties for shadow is likely too large, when I said too large I mean unbelievable large, because gap between content views in cells are no more than 15 - 30 even less. so try it with much less values, while radius can remain 10 but you will see what value fit the best. If cell content view is your custom view just values will did the job if your view is not kind of transparent or any inside it, in that case it won't, and there is hard to fix that, I tried many libraries and custom codes and it is never ok.
squircleView.layer.cornerRadius = 40
squircleView.layer.cornerCurve = CALayerCornerCurve.continuous
squircleView.layer.shadowColor = UIColor.systemGray.cgColor
squircleView.layer.shadowOpacity = 0.7
squircleView.layer.shadowOffset = CGSize(width: 0, height: 0.5)
squircleView.layer.shadowRadius = 5

Border not getting applied to full view

I am setting up the dashed border to my UIView it is getting applied in iPhone 6,7,8. But if open in iPhone XR it is coming like this.
My view is named as camerauiview
The border I am setting up in this function by calling it in viewdidload().
func setupBorder(){
let dashedborder = CAShapeLayer()
dashedborder.strokeColor = #colorLiteral(red: 0.4076032743, green: 0.1454663677, blue: 0.9795397903, alpha: 1)
dashedborder.lineWidth = 2
dashedborder.lineDashPattern = [7 , 7]
dashedborder.frame = camerauiview.bounds
dashedborder.fillColor = nil
dashedborder.path = UIBezierPath(rect: camerauiview.bounds).cgPath
camerauiview.layer.addSublayer(dashedborder)
}
This is coming like this.
For reference, I have given background color as grey just to showcase how much area it is covering.
Use func viewDidLayoutSubviews()
When the bounds change for a view controller's view, the view adjusts the positions of its subviews and then the system calls this method. However, this method being called does not indicate that the individual layouts of the view's subviews have been adjusted. Each subview is responsible for adjusting its own layout.
Your view controller can override this method to make changes after the view lays out its subviews. The default implementation of this method does nothing.
Use your func setupBorder() in override func viewDidLayoutSubviews() Method like:
override func viewDidLayoutSubviews() {
setupBorder()
}

UITableViewCells bottom edge flickering when app enters foreground

I've build an app which contains an UITableView with a bunch of cells. Inside the cells I've got a view, which fill the whole cell. I've configured the tableview like this:
tableView.separatorStyle = .none
tableView.backgroundColor = UIColor(red: 24/255.0, green: 34/255.0, blue: 41/255.0, alpha: 100)
tableView.separatorColor = UIColor(red: 26/255.0, green: 34/255.0, blue: 40/255.0, alpha: 100)
Whenever the app enters the foreground, I got those little lines flickering for 0.5 seconds or so. To be clear, I don't want those.
And this is how it looks like when the app fully entered the foreground, and how it is supposed to look like:
Any ideas how to get rid of them?
EDIT 1:
I'm starting to doubt that the flickering is related to the separators, because it is only happening between cells in a section, not between the section-cell and the first cell in a section. I've grabbed some screenshots of the view hierarchy and the constraints related to the view (Foreground view) I show in the cell.
EDIT 2:
If I set the top and bottom constraint to -2 instead of 0, there's no flickering at all, however it's not as I want it visually. So the flickering is not related to the separators at all.
Trick for removing the cell separators.
Objective-C
- (void)viewDidLoad {
[super viewDidLoad];
self.tableView.tableFooterView = [UIView new];
}
Swift
override func viewDidLoad() {
super.viewDidLoad()
tableView.tableFooterView = UIView()
}
Usually flickering happens when you're returning a wrong heightForRowAtIndexPath.
In your case, you're returning a little smaller than your cell's actual height I guess.
So try to set "clipToBounds" of your cell to "true" and check if it works.
Try setting "Renders with edge antialiasing" to YES in your info.plist.
I think here your issue with UITableViewStyle. Right now you are using UITableViewStyle Grouped. So, line between cell isn't UITableViewCellSeparator it's Group Table 1 pixel header and footer space. So,
I have two solutions:
Either use UITableView background color same as cell background color.
Change UITableView style to Plain
GroupTable SS
or
PlainTable SS
I hope it'll help you. And solve your issue :)
I think it's not related to the separator, because the separator doesn't cover the whole screen, it must be related to your constraints, try changing the background color of the BackgroundContainerView, the DepartureCell and the TableView, one of these 3 views should have the dark grey color as a background color.
Would it be possible that the tableview is inherited from another one and seperatorStyle could be set different in the super class? Then, you need override it.
Set this in viewDidLoad()
tableView.separatorStyle = .none
You can do it as per follows, from your storyboard to avoid that separator from UITableView.
If you set
tableView.separatorStyle = .none
on viewDidLoad(), it will flicker
you need to it before like in viewWillAppear() or in the storyboard
Try setting the tableview separator color with full transparency it might help
tableView.separatorColor = UIColor(red: 26/255.0, green: 34/255.0, blue: 40/255.0, alpha: 0)
Do this in viewWillAppear
If that will not help check the view hierarchy maybe there is an issue with the cell rendering in
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath)
Check this post and its swift 4 update
or try adding this extension to your ViewController
extension UITableViewCell {
func removeCellSeparators() {
for subview in subviews {
if subview != contentView && subview.frame.width == frame.width {
subview.removeFromSuperview()
}
}
}
}
then try calling it in just before you return the cell
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "YourCellIdentifier", for: indexPath)
cell.removeSeparators()
return cell
}
if this doesn't help then please post more information about your setup, code or maybe host a minimal version of your app with the tableView having the issue on gitHub so that I can help.
EDIT 1:
Try setting the rowHeight / cellHeight to 15 pixels more than what it currently is if that will solve your problem than the cellHeight is what needs tweaking could be that it only needs to be 2-4 pixels higher. Probably as the app is entering the foreground autolayout is trying to do what it can do show everything as you want however some constraints are ambiguous therefore whilst entering from the background there is the view appearing animation from the system for about half a second and there is your flickering as well.
Can you use Xcode "Debug View Hierarchy" to find question View , and use "KVC" remove that view.
ps. my english is poor , i hope i can help you
From storyboard select table view separator to None.
In Separator Inset select custom and remove left value make it from 15 to 0.
Build and run it again now check.
Make tableView's backgroundColor and separatorColor exactly the same as in:
tableView.backgroundColor = UIColor(red: 24/255.0, green: 34/255.0, blue: 41/255.0, alpha: 100)
tableView.separatorColor = UIColor(red: 24/255.0, green: 34/255.0, blue: 41/255.0, alpha: 100)

How do I add an ImageView into a table cell on button click?

I am trying to do something like this:
When the user clicks "Add Page," a new grouping shows up below it. Now, I decided to use Table View cells in order to achieve this. After following various tutorials and looking up similar Q&As, I am able to add cells on button click with UILabel and have the cell height be dynamic depending on the content but now I am trying to figure out how to add ImageViews and place buttons within a cell.
I've created a custom cell class:
class PageCell : UITableViewCell {
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.selectionStyle = UITableViewCellSelectionStyle.none
setupViews()
}
...
... // other random code here
let imgView : UIImageView = {
let imgview = UIImageView()
imgview.frame = CGRect(x: 100, y: 150, width: 150, height: 140)
imgview.tintColor = UIColor(red: 0.73, green: 0.2, blue: 0.3, alpha: 1.0)
imgview.translatesAutoresizingMaskIntoConstraints = false
return imgview
}()
func setupViews() {
addSubview(pageLabel) // the label that I got working
addSubview(imgView) // can't get this working
...
// constraint info here
}
}
And back in my TableViewController:
class TakePhotosVC: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(PageCell.self, forCellReuseIdentifier: "cellID")
}
// return the actual view for the cell
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let pagecell = tableView.dequeueReusableCell(withIdentifier: "cellID", for: indexPath) as! PageCell
// set more stuff here
}
... // more code
}
My issue is that I am trying to get a box showing where the ImageView is that the user can click on to load in a picture. I am unsure how to do that and place all the relevant buttons as well (Trash, X, etc.)
Any help would be greatly appreciated.
EDIT
Okay, I was trying to follow this tutorial and I can't quite get it to work. In my prototype cell, I see this:
But the result is this:
I made the UIImageView have a background so I can see it. I have two constraints for the UIImageView which are: width = 240, height = 128 and two constraints for the Page Label which are: width = 240, height = 21. Two questions: why are my elements not placed correctly even though I have it correctly placed in the Storyboard? And why is the cell height not dynamically resizing to accommodate the elements?
I have these two lines in my viewDidLoad method of the TakePhotosVC but it doesn't seem to do anything.
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 200
If it's relevant, I get this warning when I run the Simulator.
Warning once only: Detected a case where constraints ambiguously
suggest a height of zero for a tableview cell's content view. We're
considering the collapse unintentional and using standard height
instead.
EDIT 2
I got it to work. For any poor souls reading this after me, you have to click on those dotted pink lines in the Constraints window editor and then click "Add X Constraints" in order to get the ImageView to center and stuff.
My issue is that I am trying to get a box showing where the ImageView
is that the user can click on to load in a picture. I am unsure how to
do that and place all the relevant buttons as well (Trash, X, etc.)
I am not sure if I understand you correctly. You have a cell with an UIImageView. But you want to show a box to visually indicate where the user should touch to add an UIImage (?)
Why not simply add a UIButton on top of the UIImageView with the exact same frame size, and on touch, you fire your action to add the image, and once the image is successfully added, you can set the UIButton to hidden.
If the user deletes the image with the trash button, you simply show the UIButton again by hidden = NO.
Other solution:
Add a border to the UIImageView with custom colors and add a UITapGestureRecognizer to fire an action. (Make sure you set the UIImageView to userInteractionEnabled = YES;
You can allso add a Placeholder image to the UIImageView when there is no image set, with your custom design.
The easiest approach would be to use a xib instead of placing the buttons programmatically. To do this, add a new file and select xib. In a xib, you can pre-make a tableviewcell with the image view, and you buttons placed for you already with constraints. Then, you can subclass this table view cell and connect the image view and buttons with ib outlets and ib actions to access the buttons and image view. Then, in your cellForRow function, load the xib like this:
Bundle.main.loadNibNamed("NameOfNib", owner: self, options: nil).first as! NameOfSubclass
I would advise to read more on xibs and nibs.

Adding gradient to scrolling UITableView

I was able to add a gradient my UITableView, but I have the issue of when I have to scroll through my cells, the gradient background scrolls along also. I want the background to stay consistent as I scroll up or down. How can I achieve this? Do I have to create a custom UITableView in order to do this?
The pictures below show what it currently looks like.
Here is my code for adding the gradient to the UITableView:
func addGradientToBackground(){
var gradient : CAGradientLayer = CAGradientLayer()
gradient.frame = self.tableView.bounds
gradient.colors = [UIColor.blueColor().CGColor, UIColor.redColor().CGColor]
self.tableView.layer.insertSublayer(gradient, atIndex: 0)
}
Messing around will setting the gradient doesn't work either, like setting:
self.view.layer.insertSublayer(gradient, atIndex: 0)
or changing the bounds:
gradient.frame = self.tableView.frame
Also, in cellForRowAtIndexPath I set the UITableViewCells background color to clear:
cell.backgroundColor = UIColor.clearColor()
I can't add images, but here is the link if you wish to see them: http://imgur.com/Vtka1tO,6faLMkr#0
The gradient needs to be behind the table view if you don't want it to scroll. If you're using a UITableViewController, the only thing behind is the window, so you could give it the gradient, and make the cells and the table view have a clear background color. If you're using a UIViewController with a table view as a subview, then you could give the controller's main view a gradient background color.
Have you thought to add a background image as follows?
var imageView = UIImageView(frame: CGRectMake(10, 10, cell.frame.width - 10, cell.frame.height - 10))
let image = UIImage(named: ImageNames[indexPath.row])
imageView.image = image
cell.backgroundView = UIView()
cell.backgroundView.addSubview(imageView)

Resources