Collection View Cells alpha is changing after scroll - ios

I created a Collection View where the cells have a view inside. The views have a alpha value of 0.65. When I scroll, the view gets brighter. Maybe the views will be stacked on top of each other?
MY CODE:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! LevelStufenCell
cell.levelViewBack = UIView()
cell.levelViewBack.frame = CGRect(x: 0, y: 0, width: cell.frame.height, height: cell.frame.width)
cell.levelViewBack.layer.cornerRadius = cell.levelViewBack.frame.height * (36 / 198)
cell.levelViewBack.backgroundColor = UIColor.white
cell.levelViewBack.alpha = 0.65
cell.insertSubview(cell.levelViewBack, at: 10)
return cell
}

Cells are reused, so you are adding another levelViewBack to each cell every time the cells scroll into view.
You have defined levelViewBack inside your LevelStufenCell ... is it an IBOutlet? If so, you do not need to create a new one each time - simply remove this line:
cell.levelViewBack = UIView()
If it is not an IBOutlet, are you creating it inside LevelStufenCell? If so, again, simply remove that line. If not, check if it's been created before "creating it again":
if cell.levelViewBack == nil {
cell.levelViewBack = UIView()
}

Related

Scrollview vs Collectionview. Get "visible image"

What I want to achieve: I have an array of images that I want to horizontally swipe/scroll through. These images represent contacts. When I find the contact(image) I want, I press a separate Start Call button. This button segues to a Phone view controller. I need to display an image of the person I'm calling on that Phone view controller. To do this I need to know what contact image I was on before I initiated the segue.
My contacts view controller. Currently showing my scrollview attempt
What I currently have: I have a scrollview and a separate collectionview already set up (they are in NO WAY connected to each other. I have both because I'm trying to figure out which gets me what I need). Both are fully functional and allow me to page through images from my array.
I can’t figure out how to pragmatically get which image is being displayed when I press the button that would initiate the segue to the next view controller.
I was originally trying to tag the images and then retrieve the tag and pass it to the next view controller via segue.
I then tried to get the index of the image that is "currently visible" and pass it via segue.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ImageCollectionViewCell", for: indexPath) as! ImageCollectionViewCell
cell.imgImage.image = imageArray[indexPath.row]
//Get character for segue
selectedCharacter = indexPath.row
return cell
}
I also tried to get the image name and pass that via segue.
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ImageCollectionViewCell", for: indexPath) as! ImageCollectionViewCell
switch cell.imgImage.image{
case UIImage(named: "Van"):
selectedCharacter = 2
case UIImage(named: "Fel"):
selectedCharacter = 3
default:
selectedCharacter = 1
}
}
I have yet to be able to figure out any way to get info with the scrollview.
Scrollview viewDidLoad code:
#IBOutlet var scrollView: UIScrollView!
for i in 0..<images.count {
let imageView = UIImageView()
let x = self.view.frame.size.width * CGFloat(i)
imageView.frame = CGRect(x: x, y: 0, width: 343, height: 244)
imageView.contentMode = .scaleAspectFit
imageView.image = images[i]
imageView.tag = i
scrollView.contentSize.width = scrollView.frame.size.width * CGFloat(i + 1)
scrollView.addSubview(imageView)
}
The Question: Does anyone have any ideas on how I get which contact I'm trying to call and if I should use a scroll view or a collection view?
Thank you!
I don't know where you click, but you can get the current view index with this:
let index = scrollView.contentOffset.x / scrollView.bounds.size.width
collectionView or scrollview is the same
you can use a collection view. create a custom cell class that has an image property. when you are about to return the cell in the cellForItem function, you can set the image property.
let cell = collectionView.dequeueReusableCellWithIdentifier("Your Cell's identifier", forIndexPath: indexPath)
cell.image = imageArray[indexPath.item]
you can then override the didSelectItem, and access the image property of the cell at the indexPath that is passed in like so:
let cell = collectionView!.cellForItemAtIndexPath(indexPath)
let image = cell.image
// do whatever you need with the image.

Force new line for some items in collection view - Swift 3

I have a collection view with multiple sections. Collection view data source is
dataSource = [Section]
Each section is having data members as below
Section = {id, [arr1], [arr2]}
Here, id is assigned as section header label. Currently, I am able to generate and insert cells for all the items in arr1 and arr2. But they are continuously inserted under the section. I want to be able to insert cells in arr2 starting on a new line even if there is space left after inserting the last item in arr1. My view controller subclasses UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout. What would be the most elegant and flexible way to achieve this? Am I missing anything?
Below is what I have in collectionview(cellForItemAt: ). Resources is arr1 and ResourceDesktops is arr2.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if indexPath.item < sections[indexPath.section].resources.count {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: resourceReuseIdentifier, for: indexPath) as! FeedsViewCell
if #available(iOS 11.0, *) {
cell.imageView.accessibilityIgnoresInvertColors = true
}
cell.image = UIImage(named: sections[indexPath.section].resources[indexPath.item].imageName)
cell.resourceLabel.text = sections[indexPath.section].resources[indexPath.item].resourceName
cell.resourceLabel.sizeToFit()
var labelFrame = cell.resourceLabel.frame
labelFrame = CGRect(x: labelFrame.origin.x, y: labelFrame.origin.y, width: labelWidth, height: labelFrame.height)
cell.resourceLabel.frame = labelFrame
return cell
} else {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: resourceDesktopReuseIdentifier, for: indexPath) as! ResourceDesktopViewCell
if #available(iOS 11.0, *) {
cell.imageView.accessibilityIgnoresInvertColors = true
}
cell.resourceLabel.text = sections[indexPath.section].resourceDesktops[indexPath.item - sections[indexPath.section].resources.count].resourceName
cell.resourceLabel.sizeToFit()
var labelFrame = cell.resourceLabel.frame
labelFrame = CGRect(x: labelFrame.origin.x, y: labelFrame.origin.y, width: labelWidth, height: labelFrame.height)
cell.resourceLabel.frame = labelFrame
return cell
}
}
You can use UITableView instead of UICollectionView for whole data.
In details you can use 2 UICollectionViews and one label in each cell of UITableView

Show JSON data correctly in UITableView

I've made an UITableView and filled it with JSON data I get inside my API. I get and place all correctly but when I scroll or delete a row everything gets messed up!
Labels and images interfere; this is my code:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
var dict = productsArrayResult[indexPath.row]
let cellImage = UIImageView(frame: CGRect(x: 5, y: 5, width: view.frame.size.width / 3, height: 90))
cellImage.contentMode = .scaleAspectFit
let productMainImageString = dict["id"] as! Int
let url = "https://example.com/api/DigitalCatalog/v1/getImage?id=\(productMainImageString)&name=primary"
self.downloadImage(url, inView: cellImage)
cell.addSubview(cellImage)
let cellTitle = UILabel(frame: CGRect(x: view.frame.size.width / 3, y: 5, width: (view.frame.size.width / 3) * 1.9, height: 40))
cellTitle.textColor = UIColor.darkGray
cellTitle.textAlignment = .right
cellTitle.text = dict["title"] as? String
cellTitle.font = cellTitle.font.withSize(self.view.frame.height * self.relativeFontConstantT)
cell.addSubview(cellTitle)
let cellDescription = UILabel(frame: CGRect(x: view.frame.size.width / 3, y: 55, width: (view.frame.size.width / 3) * 1.9, height: 40))
cellDescription.textColor = UIColor.darkGray
cellDescription.textAlignment = .right
cellDescription.text = dict["description"] as? String
cellDescription.font = cellDescription.font.withSize(self.view.frame.height * self.relativeFontConstant)
cell.addSubview(cellDescription)
return cell
}
You are adding subviews multiple times while dequeuing reusable cells. What you can do is make a prototype cell either in storyboard or as xib file and then dequeue that cell at cellForRowAtIndexPath.
Your custom class for cell will look similar to this where outlets are drawn from prototype cell.
Note: You need to assign Reusable Identifier for that prototype cell.
class DemoProtoTypeCell: UITableViewCell {
#IBOutlet var titleLabel: UILabel!
#IBOutlet var descriptionLabel: UILabel!
#IBOutlet var titleImageView: UIImageView!
}
Now you can deque DemoProtoTypeCell and use accordingly.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: DemoProtoTypeCell.self), for: indexPath) as! DemoProtoTypeCell
cell.titleImageView.image = UIImage(named: "demoImage")
cell.titleLabel.text = "demoTitle"
cell.descriptionLabel.text = "Your description will go here."
return cell
}
That's because you are adding subviews to reused (so that it may already have subviews added previously) cells.
Try to check if the cell has subviews and fill in information you need, if there're no subviews then you add them to the cell.
Option 1
if let imageView = cell.viewWithTag(1) {
imageView.image = //your image
} else {
let imageView = UIImageView(//with your settings)
imageView.tag = 1
cell.addSubview(imageView)
}
Option 2
Crete UITableViewCell subclass that already has all the subviews you need.
I have used below method to remove all subviews from cell:
override func prepareForReuse() {
for views in self.subviews {
views.removeFromSuperview()
}
}
But I have created UITableViewCell subclass and declared this method in it.
you can also do one thing as #sCha has suggested. Add tags to the subviews and then use the same method to remove subview from cell:
override func prepareForReuse() {
for view in self.subviews {
if view.tag == 1 {
view.removeFromSuperview()
}
}
}
Hope this helps.
I think the other answers already mentioned a solution. You should subclass the tableview cell and just change the values of your layout elements for each row.
But I want to explain why you get this strange behaviour.
When you call
tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
it tries to reuse an already created cell with the passed identifier #"cell". This saves memory and optimises the performance. If not possible it creates a new one.
So now we got a cell with layout elements already in place and filled with your data. Your code then adds new elements on top of the old ones. Thats why your layout is messed up. And it only shows if you scroll, because the first cells got no previous cells to load.
When you subclass the cell try to create the layout only once on first initialisation. Now you can pass all values to the respective layout element and let the tableview do its thing.
Try this:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell:UITableViewCell! = tableView.dequeueReusableCell(withIdentifier: "cell")
if cell == nil
{
cell = UITableViewCell.init(style: UITableViewCellStyle.default, reuseIdentifier: "cell")
}
for subView in cell.subviews
{
subView.removeFromSuperview()
}
// Your Code here
return cell
}

UIcollectionview lagging while scrolling cells

I am creating 7 cells using UICollectionview. When i scroll, the application works fine, but if i continue to scroll, it start to lag and the shadow(behind every cell) become more dark.
I think that the cell that disappear from the screen is not deleted and when i return back the program recreate a new one in the same position of the oldest one. is there any solution?
Screen before the scroll
https://ibb.co/dNQJ5k
Scree after the scroll
https://ibb.co/kjDAJ5
here's the code
class menuController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
#IBOutlet weak var coll_view: UICollectionView!
var array = [String]()
override func viewDidLoad() {
array = ["segue_menu_map", "segue_menu_camera"]
coll_view.scrollToItem(at: IndexPath(item: 2, section: 0), at: .left, animated: true)
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 7
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
let button = UIButton(type: .custom)
button.frame = CGRect(x: 0, y: 0, width: cell.frame.width, height: cell.frame.height)
button.layer.cornerRadius = 0.2*button.frame.width
button.backgroundColor = UIColor.white
button.layer.borderWidth = 2
button.tag=indexPath.row
button.layer.shadowColor = UIColor.lightGray.cgColor
button.layer.shadowOpacity = 1.0
button.layer.shadowOffset = CGSize(width: 0.4, height: 1.8)
cell.clipsToBounds = false
button.addTarget(self, action: #selector(collectionAction(sender:)), for: .touchUpInside)
print([indexPath.row])
//cell.backgroundColor = UIColor.gray
button.setTitle(String(indexPath.row), for: .normal)
button.setTitleColor(.black, for: .normal)
cell.addSubview(button)
return cell
}
func collectionAction( sender: UIButton) {
if sender.tag < 2{
self.performSegue(withIdentifier: array[sender.tag], sender: nil)
}
}
}
thanks in advance
collectionView.dequeueReusableCell does not create a new cell every time. It reuses already created cells to improve performance. You are adding a button to the cell every time it's reused, this could mean that a single cell could have dozens/hundreds of buttons on it.
The solution is to make a subclass of UICollectionViewCell and put your set-up code there.
I faced the same problem when i am scrolling the collectionView.
I had found two major problem and fixed this.
I moved the code like create a button or something to the collectionviewcell .m file explicitly. This bring me smooth scrolling.
I delete shadow property of the cell.. trust me it removes the lag immediately.
Now i dont know how to fix this problem with shadow offset. but upper case was fixed the lag.
And add this code in custom cell or cellforItem in main class:
for main class
cell.layer.shadowPath = [UIBezierPath bezierPathWithRoundedRect:cell.bounds cornerRadius:cell.contentView.layer.cornerRadius].CGPath;
for custom cell
self.layer.shadowPath = [UIBezierPath bezierPathWithRoundedRect:cell.bounds cornerRadius:cell.contentView.layer.cornerRadius].CGPath;

UICollectionView reload data doesn't update cells

I'd like to preface this by saying I'm used to how UITableViews work and am not super familiar with UICollectionView.
What I'm essentially doing is
Add views (including collection view) on screen
featuredCollectionView = UICollectionView(frame: CGRect(x: 0, y: featuredLabel.frame.origin.y + featuredLabel.frame.size.height, width: view.frame.size.width, height: 216), collectionViewLayout: layout)
featuredCollectionView.delegate = self
featuredCollectionView.dataSource = self
featuredCollectionView.register(ItemCollectionViewCell.self, forCellWithReuseIdentifier:
"cell")
featuredCollectionView.backgroundColor = UIColor.fysGray
featuredCollectionView.showsHorizontalScrollIndicator = false
// add view to screen
Load data from network and add to datasource array
func fetchPosts()
{
// note some libs have been changed for x purposes
NetworkClass.loadPosts()
{ (data, error) in
// parse into valid object
// add to datasource array
self.featuredItems.append(item)
// reload data
DispatchQueue.main.async
{
self.featuredCollectionView.reloadData()
}
}
collectionView.reloadData() on main queue as shown above
After calling reload data nothing appears on screen as if it is blank. I've tried reloading from the main queue and inserting/performing batch updates etc. I'm just really confused on how to get this working. Do I have to know the number of items before I load from the network? How can I get this working?
Here if the code for cellForItemAt
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
var item: Item
if collectionView == featuredCollectionView
{
item = featuredItems[indexPath.row]
}
else
{
item = recentlyViewedItems[indexPath.row]
}
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! ItemCollectionViewCell
cell.item = item
return cell
}
Ended up being a problem with the if condition in the cell method. It didn't like it for some reason, so I used two separate view controllers with a container view and it worked like a charm with reloadData()

Resources