imageView.layoutMargins = UIEdgeInsets.zero has no effect in UICollectionViewCell - ios

I have some configuration based on the type of data I receive ( the insets to add in the cell.configure(with item: item) {}, however the cells are not re-used properly. I am using RxSwift for binding, though I don't see the issue in there.
tried setting image.layoutMargins = UIEdgeInsets.zero in prepareForReuse(), in the cell.configure(with: item) etc. Doesn't seem to work.
brandsGroup
.bind(to: rx.items(cellIdentifier: StudioCellConstants.brandReuseIdentifier,
cellType: BrandCollectionViewXibCell.self)) { (_, item, cell) in
cell.configure(with: item)
}.disposed(by: disposeBag)
override func prepareForReuse() {
super.prepareForReuse()
image.layoutMargins = UIEdgeInsets.zero
}
Update:
Eventually I ended up setting back the frame like this, but it feels like a hacky solution.
A better way would be much appreciated.
image.frame = CGRect(origin: CGPoint(x: 0, y: 0), size: CGSize(width: imageContainerView.frame.width, height: imageContainerView.frame.height)

Are you setting the content fill mode of imageview in cell correctly.
//Swift code(I am not familier in rxswift but the concept should be the same)
let imageView = UIImageView()
imageView.contentMode = .scaleAspectFill
I prefered scaleAspectFill here as I assume u need the image to cover the whole imageView

Related

UITableViewCell hides part of content after appearing on the screen with custom layoutSubviews()

I'm struggling with UITableView. As you can see in this video in third section of table view third cell isn't display correctly. That happens when I dequeue my cell like that:
let cell = tableView.dequeueReusableCell(withIdentifier: MultipleSelectAnswerSurveyTableViewCellIdentifier, for: indexPath) as! MultipleSelectAnswerSurveyTableViewCell
cell.setup(answer: question.answers?[indexPath.row].value ?? "", isSelected: false, style: style, isLastInSection: indexPath.row == (question.answers?.count ?? 1) - 1)
return cell
Cell's setup() method:
func setup(answer: String, isSelected: Bool, style: Style, isLastInSection: Bool) {
self.isLastInSection = isLastInSection
selectionStyle = .none
backgroundColor = style.survey.singleSelectAnswerTableViewCell.backgroundColor
answerLabel.textColor = style.survey.singleSelectAnswerTableViewCell.answerLabelColor
answerLabel.font = style.survey.singleSelectAnswerTableViewCell.answerLabelFont
answerLabel.text = answer
addSubview(answerLabel)
addSubview(selectionIndicator)
answerLabel.snp.makeConstraints { make in
make.left.equalTo(8)
make.centerY.equalTo(selectionIndicator.snp.centerY)
make.top.equalTo(8)
make.bottom.equalTo(-8)
make.right.equalTo(selectionIndicator.snp.left).offset(-8)
}
selectionIndicator.snp.makeConstraints { make in
make.right.equalTo(-8)
make.top.greaterThanOrEqualTo(8)
make.bottom.lessThanOrEqualTo(-8)
make.width.height.equalTo(26)
}
}
self.isLastInSection variable is used inside layoutSubviews():
override func layoutSubviews() {
super.layoutSubviews()
if isLastInSection {
roundCorners(corners: [.bottomLeft, .bottomRight], radius: 16.0)
}
contentView.layoutIfNeeded()
}
And finally roundCorners():
extension UIView {
func roundCorners(corners: UIRectCorner, radius: CGFloat) {
let path = UIBezierPath(roundedRect: bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
let mask = CAShapeLayer()
mask.path = path.cgPath
layer.mask = mask
}
}
When I dequeue cell with isLastInSection set to false cell is being displayed as expected (related video). So I think the problem is in life cycle of the cell and when the layoutSubview() is being called. I tried many solutions for similar problem found in different threads but none of them helped me. tableView(_:heightForRowAt:) causes the third cell to display correctly, but the first one has rounded bottom corners. Also all of them are fixed in height and that cannot happen.
But what is really weird: when I print the isLastInSection during dequeueing cell which is unexpectedly rounded debugger returns me false:
(lldb) po indexPath.row == (question.answers?.count ?? 1) - 1
false
As you can see in Debug View Hierarchy view text exists so that's why I 've defined the problem as hiding part of content.
You dequeue cell and each time you add subviews you don't check if they are already there which will happen in case of recycled cell. That probably breaks constraints and causes incorrect sizing.
Same problem with rounding - you set rounded corners, but you never revert this behavior when reused cell should not be rounded.
Best way to solve this issue would be to add additional check and create subviews only once:
func setup(answer: String, isSelected: Bool, style: Style, isLastInSection: Bool) {
if self.subviews.count == 0 {
// adding subviews etc.
// code that needs to be performed only once for whole cell's life
}
self.isLastInSection = isLastInSection
// set up content that changes for each cell (like text)
// for example a code depending on parameters of this method
}
alternatively you could keep some property like isInitialized and check that at the beginning.
Also your method layoutSubviews must support both cases:
override func layoutSubviews() {
super.layoutSubviews()
if isLastInSection {
roundCorners(corners: [.bottomLeft, .bottomRight], radius: 16.0)
} else {
layer.mask = nil
}
contentView.layoutIfNeeded()
}

scrolling until last cel of uicollectionview

I have a problem while scrolling my collectionView
after choosing a cell, the last cell is not available for choosing
func changeViewSize(from: CGFloat, to: CGFloat, indexPath: IndexPath) {
UIView.transition(with: myCollection, duration: 0.5, options: .beginFromCurrentState, animations: {() -> Void in
let cell = self.myCollection.cellForItem(at: indexPath)!
let cellCenter = CGPoint(x: cell.center.x, y: cell.center.y + 50)
if cell.bounds.height == from {
let sizee = CGRect(x: cell.center.x, y: cell.center.y, width: cell.frame.width, height: to)
self.myCollection.cellForItem(at: indexPath)?.frame = sizee
self.myCollection.cellForItem(at: indexPath)?.center = cellCenter
for x in self.indexPathss {
if x.row > indexPath.row {
print("this is cells")
print(self.myCollection.visibleCells)
let cell = self.myCollection.cellForItem(at: x)!
cell.center.y += 100
}
}
}
}, completion: {(_ finished: Bool) -> Void in
print("finished animating of cell!!!")
})
}
It looks like you're changing cell sizes and positions manually. The UICollectionView won't be aware of these changes and therefore its size is not changing. The last cells simply move out of the visible area of the collection view.
I haven't done something like this before, but have a look at performBatchUpdates(_:completion:). I'd try to reload the cell that you want to make bigger using that method. Your layout will have to supply the correct attributes, i.e. size.
From the screenshots it looks like maybe you could use a UITableView instead? If that's a possibility it might simplify things. It also has a performBatchUpdates(_:completion:) method, and the UITableViewDelegate would then have to provide the correct heights for the rows.
Because of spacing of cell we used collectionView and I think changing height of tableviewcell is easier than collectionviewcell if you have an idea for spacing of tablviewcell wi will be glad to have your idea.

UIGraphicsGetImageFromCurrentImageContext() returning blank image in UITableViewCell

I've been successfully using the function below to take snapshots of UIViews, however, now that I'm using it inside a UITableViewCell, it no longer works.
static func pictureTaker(_ rawView: UIView) -> UIImageView {
UIGraphicsBeginImageContextWithOptions(rawView.bounds.size, false, 0)
rawView.drawHierarchy(in: rawView.bounds, afterScreenUpdates: true)
let screenshot = UIGraphicsGetImageFromCurrentImageContext();
let viewImage = UIImageView(image: screenshot)
UIGraphicsEndImageContext();
viewImage.frame = rawView.frame
return viewImage
}
Here is a trimmed down version of the function I'm using to populate the UITableViewCell.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = gamesHolder.dequeueReusableCell(withIdentifier: textCellIdentifier, for: indexPath) as! StandardGameCell
let row = indexPath.row
let boardPreviewTemplate = UIView(frame: CGRect(x: 0, y: 0, width: cell.previewBoard.frame.width, height: cell.previewBoard.frame.height))
GameManagement.setupBoard(boardPreviewTemplate, boardItems: gamesArray[row].board)
let gamePicture = PageManagement.pictureTaker(boardPreviewTemplate)
gamePicture.frame = CGRect(x: 0, y: 0, width: gamePicture.frame.width, height: gamePicture.frame.height)
//Removing preview board items and replacing with picture
cell.previewBoard.subviews.forEach({ $0.removeFromSuperview() })
cell.previewBoard.addSubview(gamePicture)
return cell
}
In the above tableView function, I'm programmatically creating a new view to replicate cell.previewBoard, but I also tried directly snapshotting cell.previewBoard and that didn't work either. I think the issue has something to do with the timing of the UITableViewCell loading.
I'm also 100% positive that the function is returning an image (but a blank one) and that it's where I'm intending it to be. If I change UIGraphicsBeginImageContextWithOptions(rawView.bounds.size, false, 0) to UIGraphicsBeginImageContextWithOptions(rawView.bounds.size, true, 0), the returned image shows up as entirely black.
As one final note, I discovered that boardPreviewPicture.snapshotView(afterScreenUpdates: true) would return an image, but it made scrolling through the UITableView very slow. Maybe there is another workaround where I can use snapshotView() instead.
I ran into the same problem. What I noticed is that when UIGraphicsBeginImageContextWithOptions is called inside tableView(_:cellForRowAt:), the result from UIGraphicsGetImageFromCurrentImageContext() is blank. Take note that in the documentation it is stated that UIGraphicsGetImageFromCurrentImageContext:
This function may be called from any thread of your app.
However, the solution that I discovered is to call it inside the main thread. In your case, maybe you can do something like:
DispatchQueue.main.async {
let gamePicture = PageManagement.pictureTaker(boardPreviewTemplate)
gamePicture.frame = CGRect(x: 0, y: 0, width: gamePicture.frame.width, height: gamePicture.frame.height)
//Removing preview board items and replacing with picture
cell.previewBoard.subviews.forEach({ $0.removeFromSuperview() })
cell.previewBoard.addSubview(gamePicture)
}

Why is the table view scrolling lagging?

I implemented a table view, but the scrolling is lagging. Why is it lagging? I am reusing the cell, but it is still lagging. When I scroll, the memory spikes and comes back to normal when the scrolling stops. If it is of any use, I implemented a collectionview inside of the table view cell.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell: BoxCell = table.dequeueReusableCellWithIdentifier("CELL", forIndexPath: indexPath) as! BoxCell
cell.pic.image = pic[indexPath.row].circle
return cell
}
extension UIImage {
var circle: UIImage? {
let square = CGSize(width: min(size.width, size.height), height: min(size.width, size.height))
let imageView = UIImageView(frame: CGRect(origin: CGPoint(x: 0, y: 0), size: square))
imageView.contentMode = .ScaleAspectFill
imageView.image = self
imageView.layer.cornerRadius = square.width/2
imageView.layer.masksToBounds = true
UIGraphicsBeginImageContext(imageView.bounds.size)
guard let context = UIGraphicsGetCurrentContext() else { return nil }
imageView.layer.renderInContext(context)
let result = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return result
}
}
How big are your images? If they are big images table will slowdown. I have recently encountered with that problem. Using smaller images make it faster again.
A lot is happening in this cell maybe tone down its resources. For example the collection view is being reloaded every time you are scrolling through the table view. "cell.collectionview.reloadData()". Maybe take the contents of the cell and load it once in an Awake From Nib custom cell instead of calling all these operators, image and coordinates for every cell each time you are scrolling through your table view. The memory is spiking because you are also reloading your collection view, no matter how large it is, every time you are scrolling. Everything is being handled on the main thread as well. Maybe move some image layout code to a background thread for a smoother scrolling action.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
//Image work here (including image if statement)
})
Ideally move the image loading to awake from nib so it load once.

Images in UIImageView not showing in UITableView when circular mask is applied to the ImageView unless scroll

Thanks in advance for the help.
I have a UITableView within a main view contoller. Within the prototype cell, I have a UIImageview. In the code below everything works until I add the 5 lines to apply a circular mask and border. Once I do that, the images will not load unless I scroll the cells. The mask and border do get applied perfectly however. Will be great when it works... but until then.
Certainly this has been seen before. I'm a swift/objective-C newbie.
Working in swift for this one.
Code below;
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("mixerCell", forIndexPath: indexPath) as! MixerTableViewCell
// set label background color to clear
cell.textLabel?.backgroundColor = UIColor.clearColor()
// set highlight selection to none
cell.selectionStyle = .None
// set image for cell
let imageView = cell.viewWithTag(1) as! UIImageView
// put circular mask and border. This is the problem code that causes initial load of the tableview images to show up blank.
imageView.layer.cornerRadius = imageView.frame.size.width / 2;
imageView.clipsToBounds = true;
let color1 = UIColor(white: 1.0, alpha: 0.5).CGColor as CGColorRef
imageView.layer.borderWidth = 2;
imageView.layer.borderColor = color1
// assign image
imageView.image = UIImage(named: mixerSounds[indexPath.row])
return cell
}
initial view load
after scroll
your code is perfectly working for me. Here i am using Xcode-7. i think you are using Xcode-6.3 or less version. just upgrade it to Xcode- 7. and if you are using the same then just check your heightforRowAtIndexpath or other delegates there should be some issue.
thanks
Try changing the below lines,
// replace this
let imageView = cell.viewWithTag(1) as! UIImageView
// to
let imageView = cell.yourImageViewName
/* yourImageViewName is the outlet
reference name you have given in the
MixerTableViewCell custom class.
*/
Edit 2: Just for debugging purposes,
hardcode the image name and check if the image appears on the all the cells.
imageView.image = UIImage(named: "first1.png")
override func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell!
{
let cellIdentifier = "cell"
var cell : UITableViewCell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier) as UITableViewCell
cell.image_View.image = UIImage(named: mixerSounds[indexPath.row])
println("The loaded image: \(image)")
cell.image_View.layer.masksToBounds = false
cell.image_View.layer.borderColor = UIColor.blackColor().CGColor
cell.image_View.layer.cornerRadius = image.frame.height/2
cell.image_View.clipsToBounds = true
return cell
}
Give imageview outlet to cell and not give imageview name because by default name is imageview so take diffrent name
It looks like the problem is using clipToBounds = true I am facing the same issue while making circular UIImageView inside UITableViewCell
I didn't find the exact solution but for now I found a way to do this
if (indexPath.row == indexPath.length && !isTableReloaded)
{
let dispatchTime: dispatch_time_t = dispatch_time(DISPATCH_TIME_NOW, Int64(0.000000001 * Double(NSEC_PER_SEC)))
dispatch_after(dispatchTime, dispatch_get_main_queue(), {
self.reloadTableView()
})
}
func reloadTableView()
{
isTableReloaded = true
self.tableViewContacts.reloadData()
}
Here isTableReloaded is a Bool type var which is initialized to false in viewDidLoad()
and the if condition is to be placed at the last of cellForRowAtIndexPath but before return statement
This will resolve our problem but do not rely on this as this is not the best approach.
Please post solution for this if any one found the better approach.
Here is a perfect and state away solution for circular image in UITableview Cell.
Simply modify your UITableviewCell (custom cell) class with below code.
override func awakeFromNib() {
super.awakeFromNib()
imgEvent.layer.frame = (imgEvent.layer.frame).insetBy(dx: 0, dy: 0)
imgEvent.layer.borderColor = UIColor.gray.cgColor
imgEvent.layer.cornerRadius = (imgEvent.frame.height)/2
imgEvent.layer.masksToBounds = false
imgEvent.clipsToBounds = true
imgEvent.layer.borderWidth = 0.5
imgEvent.contentMode = UIViewContentMode.scaleAspectFill
}
It will also helps to solve the problem of image circular only after scrolling table..(if any!)
let width = cell.frame.size.width
cell.img.layer.cornerRadius = width * 0.72 / 2
0.72 is the ratio of the cell width to image width, for eg. cellWidth = 125 and imageWidth = 90, so 125/90 would give 0.72. Do similar calculation for your image.
First: Images doesn't load until you scroll, because when cellForRowAtIndexPath methods called the constraints doesn't set for image until now, so when scrolling the constraints was added and the image appears, so if you set proportional width and height for imageView (width==height) in cell then
do that
let w = tableview.frame.width*(proportional value like 0.2)
imageView.layer.cornerRadius = w / 2
imageView.clipsToBounds = true;

Resources