How to design custom collection view programatically? - ios

In my project I want to design custom View Cell programatically with SVGKImageView & UILabel like this
here square is an SVGKImageView & "Dadar" is an UILabel.
But now I am getting output like this
This is my code
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell
{
// intialize collection cell with media collection cell
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("mediaCell", forIndexPath: indexPath) as! ViewContentMediaCollectionCell
// get the data from view model
let viewModelobj = mViewContentViewModelObj!.getViewContentData()
// set the page imageview frame with default size
var mSVGImageView = SVGKFastImageView(frame: CGRect(x: 0, y: 0, width: cell.frame.width, height: (50)))
// set the pageImageview with actual pageImage
mSVGImageView = SVGKFastImageView(SVGKImage: viewModelobj[indexPath.row].mContentPage)
// add page imageview in the cell
cell.contentView.addSubview(mSVGImageView)
// add the title label
let titleLabel : UILabel = UILabel(frame: CGRect(x: 0, y: cell.frame.height - 20, width: cell.frame.width, height: 20))
// set the page title
titleLabel.text = viewModelobj[indexPath.row].mPageTitle
// add title label to the collection view cell
cell.contentView.addSubview(titleLabel)
// return the cell
return cell
}

You should create a custom view that contains an ImageView and a label like your design then add to the contentView

Related

Corner radius not properly fit in dynamic grouped tableView swift

I have created a Grouped TableView dynamically based on data. Based on data tableView cell generated automatic height so every cell has different rowHeight. I have set it accordingly by using self.tableView.rowHeight = 50
But Issue is I am using corner radius, but I don't want to use corner radius on every cell.
I am using grayBox UIView and all cells displayed in it. Corner radius apply to start of cell or grayBox and only at end of cell of grayBox but it applied to every cell. How can I do that corner radıus apply on start and bottom?
viewDidLoad() code for tableView Row Height
self.tableView.rowHeight = 50
Dynamic Grouped TableView code:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
cell.backgroundColor = UIColor.clear
cell.selectionStyle = .none
let grayBox = UIView(frame: CGRect(x: 5, y: 0, width: self.view.frame.size.width - 11, height: 50))
grayBox.backgroundColor = ("#cfd8dc").toColor()
grayBox.layer.cornerRadius = 5
grayBox.layer.borderColor = UIColor(red:0.80, green:0.80, blue:0.80, alpha:1.0).cgColor
grayBox.layer.borderWidth = 1.0
cell.contentView.addSubview(grayBox)
return cell
}
1- Use dequeue
let cell = tableView.dequeueReusableCell(withIdentifier: "cell")!
Instead of
let cell = UITableViewCell()
2- Clear subviews here by removing with tag
let grayBox = UIView(frame: CGRect(x: 5, y: 0, width: self.view.frame.size.width - 11, height: 50))
grayBox.tag = 333
cell.contentView.subviews.forEach {
if $0.tag == 333 {
$0.removeFromSuperview()
}
}
cell.contentView.addSubview(grayBox)
3- For corner raduis
if indexPath.row == 0 || indexPath.row == arr.count - 1 {
grayBox.layer.cornerRadius = 5
}
else {
grayBox.layer.cornerRadius = 0
}

ios swift 3 delete contents of each cell when reloading tableView

I am creating a quizApp using DLRadioButton with a label in each cell of tableView, the label is set from the storyBoard(with a tag) but I have set radioButtons programmatically for better design when I click next button to pass to next question new Radiobutton overlaps with the old Radiobutton
here is the first question
here is the second image
Below is my code
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell : UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "cell")!
var reponse = rep[(indexPath as NSIndexPath).row]
let content:UILabel=cell.viewWithTag(111) as! UILabel
//var ra:UIButton=cell.viewWithTag(112) as! UIButton
content.text=reponse["rep"] as? String
content.sizeToFit()
let repiii = reponse["rep"] as? String
// var radio = DLRadioButton()
if (self.type=="case à cocher"){
// cell.contentView.delete(radioButtons)
let frame = CGRect(x: 50, y: 20, width: 200, height: 40)
let squareButton = self.createRadioButton(frame: frame, title:repiii!, color: UIColor.red,compt: squareButtons.count,sqaure:1);
squareButtons.append(squareButton)
cell.contentView.addSubview(squareButton)
}
else{
let frame = CGRect(x: 50, y: 20, width: 200, height: 40)
let radioButton = self.createRadioButton(frame: frame, title:repiii!, color: UIColor.blue,compt: radioButtons.count,sqaure:0);
//radioButton.isMultipleSelectionEnabled = true
print(radioButtons.count)
radioButtons.append(radioButton)
cell.contentView.addSubview(radioButton)
}
return cell
}
any suggestion is appreciated
in your code, just after the line
let cell : UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "cell")!
add this:
for subview in cell.contentView.subviews {
subview.removeFromSuperView()
}

Image and scrollView rendering making my tableview choppy

There may be no good solution for my problem, but I want to ask just in case.
I have a tableview in which each cell contains a horizontal scrollview of variable width. The width of each cell's scrollview depends on the number and sizes of the images for that cell. Everything works pretty well, but the tableview scrolls less smoothly than it could. I'm using Parse to retrieve the images (and pfquertableviewcontroller), but this question should apply to tableviews in general.
Here is my tableview code:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath, object: PFObject?) -> PFTableViewCell? {
if var cell:MainFeedTableViewCell? = tableView.dequeueReusableCellWithIdentifier(self.cellIdentifier) as? MainFeedTableViewCell{
if(cell == nil) {
cell = NSBundle.mainBundle().loadNibNamed("MainFeedTableViewCell", owner: self, options: nil)[0] as? MainFeedTableViewCell
}
cell?.parseObject = object
return cell
}
}
override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
if let c = (cell as? MainFeedTableViewCell){
c.setUpObject()//this is where images are set
}
}
override func tableView(tableView: UITableView, didEndDisplayingCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
if let c = (cell as? MainFeedTableViewCell){
c.resetObject() //this sets most of the properties to nil
}
}
And here is the function in my cell where images are set
func setUpObject(){
//I left out several lines of code where label text is set from the parseObject that is set when the cell is created
//setting images **problems here**
if let numImages = self.parseObject?["numImages"] as? Int{
self.newWidth1 = self.parseObject?["width1"] as? CGFloat
self.newWidth2 = self.parseObject?["width2"] as? CGFloat
self.newWidth3 = self.parseObject?["width3"] as? CGFloat
self.newWidth4 = self.parseObject?["width4"] as? CGFloat
self.newWidth5 = self.parseObject?["width5"] as? CGFloat
if numImages == 1{
self.containerView = UIView(frame: CGRect(x: self.scrollView.frame.minX, y: self.scrollView.bounds.minY - 90, width: self.scrollView.frame.width, height: self.scrollView.frame.height))
self.image1 = PFImageView(frame: CGRect(x: self.scrollView.frame.minX , y: self.scrollView.frame.minY, width: self.scrollView.frame.width, height: self.scrollView.frame.height))
self.image1?.contentMode = UIViewContentMode.ScaleAspectFit
self.image1!.file = self.parseObject?["image"] as? PFFile
self.image1!.loadInBackground()
self.containerView!.addSubview(self.image1!)
self.scrollView.contentSize = self.containerView!.bounds.size
self.scrollView.addSubview(self.containerView!)
}
else if numImages == 2{
if self.newWidth1 + self.newWidth2 < self.scrollView.frame.width{
self.containerView = UIView(frame: CGRect(x: self.scrollView.frame.midX - (self.newWidth1 + self.newWidth2)/2, y: self.scrollView.bounds.minY - 90, width: (self.newWidth1 + self.newWidth2), height: self.scrollView.frame.height))
}
else{
self.containerView = UIView(frame: CGRect(x: self.scrollView.frame.minX, y: self.scrollView.bounds.minY - 90, width: (self.newWidth1 + self.newWidth2), height: self.scrollView.frame.height))
}
self.image1 = PFImageView(frame: CGRect(x: self.scrollView.frame.minX , y: self.scrollView.frame.minY, width: self.newWidth1, height: self.scrollView.frame.height))
self.image1!.file = self.parseObject?["image"] as? PFFile
self.image1!.loadInBackground()
self.containerView!.addSubview(self.image1!)
self.image2 = PFImageView(frame: CGRect(x: (self.scrollView.frame.minX + self.newWidth1), y: self.scrollView.frame.minY, width: self.newWidth2, height: self.scrollView.frame.height))
self.image2!.file = self.parseObject?["image2"] as? PFFile
self.image2!.loadInBackground()
self.containerView!.addSubview(self.image2!)
self.subLayer = CALayer()
self.subLayer.backgroundColor = UIColor.whiteColor().CGColor
self.subLayer.frame = CGRect(x: self.newWidth1, y: self.scrollView.frame.minY, width: 1, height: self.scrollView.frame.height)
self.containerView!.layer.addSublayer(self.subLayer)
self.scrollView.contentSize = self.containerView!.bounds.size
self.scrollView.addSubview(self.containerView!)
}
//repeat similar code for cases where there are 3, 4, or 5 images
There might be a fundamental issue with dynamically adjusting the size of the scrollview and adding it to superview just in time, but I'm trying to follow the design mockup that my designer gave me.
Here is what the scrollview on the cell looks like (with each image in the scrollview separated by a thin white line)
Remove your willDisplayCell and didEndDisplayingCell. That will fire as you scroll and your setUpObject code, while not huge, will block the main thread slightly. Instead move setUpObject() to right before returning the cell in cellForRowAtIndexPath.
Depending on how many rows you have and performance requirements you could also adjust the viewController to download all of the images ahead of time and pass them to the cell instead of loading them inside the cell.

UICollectionView Refresh Issue

Using UICollectionView I add a Label to a cell as a subView with the scroll direction set to horizontal. Inside the Label I add a button whose background is an image. For some odd reason if I scroll the view in one direction and then come back the buttons image seems to either have left remnants to the right of the button. Either that or their is another button slightly shifted to the right under the initial buttons. I have realized the more I played around with the buttons the further right they shift. Any assistance would be appreciated
The issue does not seem to occur if the label is shorter
var padding = String(count: 2, repeatedValue: (" " as Character))
let newLabel = UILabel(frame: CGRectZero)
newLabel.autoresizingMask = .FlexibleHeight
newLabel.backgroundColor = UIColor(red: 56/255, green: 143/255, blue: 212/255, alpha: 1)
newLabel.text = "\(padding)\(title)\(padding)"
newLabel.textColor = UIColor.whiteColor()
let fontName: CFStringRef = "Superclarendon-Regular"
newLabel.font = CTFontCreateWithName(fontName, 15, nil)
newLabel.adjustsFontSizeToFitWidth = true
newLabel.clipsToBounds = true
newLabel.layer.cornerRadius = 4
//Fit TO Text
newLabel.numberOfLines = 1
newLabel.sizeToFit()
//Add Button
if let image = UIImage(named: "Nav_Button_X"){//?//.CGImage
let button = UIButton.buttonWithType(UIButtonType.Custom) as! UIButton
var imageWidth = image.size.width
var imageHeight = image.size.height
//Resize label for button
var oldLabelFrame = newLabel.frame
var buttonHeight = self.frame.height - self.sectionInsets.top - self.sectionInsets.bottom
var buttonWidth = newLabel.frame.width + imageWidth
//newLabel.frame = CGRect(x: oldLabelFrame.origin.x, y: oldLabelFrame.origin.y, width: oldLabelFrame.width, height: bh)
newLabel.frame.size = CGSize(width: buttonWidth, height: buttonHeight)
button.frame = CGRectMake(newLabel.frame.width - imageWidth, 0, imageWidth, newLabel.frame.height)
button.setBackgroundImage(image, forState: UIControlState.Normal)
newLabel.addSubview(button)
}
return newLabel
}
EDIT:
I have tried recreating the view using only a button and NSMutableAttributedString for styling, which may or may not be a better solution shrugs, and the issue persists so it may not be an issue of how I construct the button. Are there suggestions?
I subclass and set up the UICollectionView like so
let flowLayout:UICollectionViewFlowLayout = UICollectionViewFlowLayout();
flowLayout.scrollDirection = UICollectionViewScrollDirection.Horizontal
super.init(frame: CGRectMake(0, 0, width, height), collectionViewLayout: flowLayout);
self.registerClass(UICollectionViewCell.self, forCellWithReuseIdentifier: reuseIdentifier);
self.autoresizingMask = UIViewAutoresizing.FlexibleWidth
self.backgroundColor = UIColor.whiteColor()
self.bounces = true
self.layer.cornerRadius = 5
self.scrollEnabled = true
self.delegate = self;
self.dataSource = self;
self.backgroundColor = UIColor.whiteColor();
and
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell:UICollectionViewCell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as! UICollectionViewCell;
cell.backgroundColor = UIColor.clearColor();
let cellItem = subCategories[indexPath.row]
cell.contentView.addSubview(cellItem)
// Configure the cell
return cell
}
Another interesting trait is that I removed the button from odd numbered labels and found that when I perform the action to get the issue the buttons appear on all of the labels
This looks like your cells are being reused and they show views from old cells.
When you call dequeueReusableCellWithReuseIdentifier, the method returns a cell that is not visible anymore in the UICollectionView, but it doesn't clear it's content.
Let's say you have cells A, B and C visible, you add subviews to all of them (with cell.contentView.addSubview(cellItem)). When you scroll until cell A isn't visible anymore, and you call dequeueReusableCellWithReuseIdentifier to get a new cell (call it D), you'll reuse cell A, so it'll have the subviews you added before to cell A, and the subviews you add now to cell D.
To get rid of this, you have two options:
If you're using a custom UICollectionViewCell, you should override prepareForReuse method. There you should remove all contentView's subviews.
If you're using a UICollectionViewCell, you should remove all contentView's subviews before calling cell.contentView.addSubview(cellItem)
If you want to see if you're adding the button multiple times to reused cells, you can reproduce the issue and use the View Hierarchy inspector:
I created a small project to illustrate the issue of "dirty" cells: https://github.com/lucaslt89/DirtyUICollectionViewCellsExample
In my case, I created the view hierarchy in the init method of the cell.
When scrolling slowly, the cells along with the multiline labels would appear perfectly. However, when scrolling too quickly, labels would have the size of the reused cell's label, thus appearing very bad.
I was setting preferredMaxLayoutWidth correctly, but nevertheless the cells would have issues on fast scrolling.
My problem was solved by calling setNeedsLayout inside prepareForReuse:
override func prepareForReuse() {
setNeedsLayout()
super.prepareForReuse()
}

change width of tableviewcell swift

I have a tableView using IB with custom cells and prototype cells.
I'm trying to make the cells a little shorter in width than the tableView.frame to leave a little space between the left and right corners.
var cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as TableViewCell
cell.bounds = CGRectMake(10, self.tableView.frame.origin.y, 30, self.tableView.frame.size.height)
cell.layer.bounds = CGRectMake(10, self.tableView.frame.origin.y, 30, self.tableView.frame.size.height)
cell.textLabel?.bounds = CGRectMake(10, self.tableView.frame.origin.y, 30, self.tableView.frame.size.height)
Update: here is a good example explaining how to add a subView to your tableView.
http://natashatherobot.com/ios-frame-vs-bounds-resize-basic-uitableview-cell/
Update 2: Looks like there isn't an easy way to do this. There are 3 ways of achieving this as far as I know:
Add a rounded and a shorter image to your cell that has the same exact color and matches your background.
You could subclass tableViewCell and then play with the layoutSubviews, this way you can make it shorter before it draws the cell. I've done it but the scrolling performance sucks.
The best way is to ditch the tableView altogether and re-do it with a collectionView.
The cells in the tableview are supposed to be as wide as their container.
If you need your cells to have a different width than the table view, I would suggest adding a view as subview to cell.contentView and make that view as wide as you need while making sure the contentView has clear background and no separator and all (so that it appears it is not there).
Another solution would be to have the tableView not as wide as it's superview by adding the left/right padding to it. But the you would have the issue that on the left and right side, where the padding is, you won't be able to scroll the tableView
I consider the cleanest solution to use a collectionView. It is not that much different than a tableView and you can configure the entire size of the cell, not just the height.
Hope this helps you fix your problem. Let me know if you need more help.
Swift 3:
For people still looking for a good solution, there's an easier and more effective alternative to using layoutSubviews or re-doing the whole thing with collectionView.
If you have already subclassed the tableViewCell, then in your TableViewController class you can add this to add a plain white border to each side of the table view cell.
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// make sure in storyboard that your cell has the identifier "cell" and that your cell is subclassed in "TableViewCell.swift"
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") as! TableViewCell
// the following code increases cell border on all sides of the cell
cell.layer.borderWidth = 15.0
cell.layer.borderColor = UIColor.white.cgColor
return cell;
}
If you want to add different sized borders to each side of the cell, you can also do this:
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") as! TableViewCell
// the following code increases cell border only on specified borders
let bottom_border = CALayer()
let bottom_padding = CGFloat(10.0)
bottom_border.borderColor = UIColor.white.cgColor
bottom_border.frame = CGRect(x: 0, y: cell.frame.size.height - bottom_padding, width: cell.frame.size.width, height: cell.frame.size.height)
bottom_border.borderWidth = bottom_padding
let right_border = CALayer()
let right_padding = CGFloat(15.0)
right_border.borderColor = UIColor.white.cgColor
right_border.frame = CGRect(x: cell.frame.size.width - right_padding, y: 0, width: right_padding, height: cell.frame.size.height)
right_border.borderWidth = right_padding
let left_border = CALayer()
let left_padding = CGFloat(15.0)
left_border.borderColor = UIColor.white.cgColor
left_border.frame = CGRect(x: 0, y: 0, width: left_padding, height: cell.frame.size.height)
left_border.borderWidth = left_padding
let top_border = CALayer()
let top_padding = CGFloat(10.0)
top_border.borderColor = UIColor.white.cgColor
top_border.frame = CGRect(x: 0, y: 0, width: cell.frame.size.width, height: top_padding)
top_border.borderWidth = top_padding
cell.layer.addSublayer(bottom_border)
cell.layer.addSublayer(right_border)
cell.layer.addSublayer(left_border)
cell.layer.addSublayer(top_border)
return cell;
}
Hope this helps.
I found the other answers unhelpful/incorrect, but found what I needed in one of the answers here:
How to set the width of a cell in a UITableView in grouped style
The key is, if you have a custom cell (which OP has, and most apps would have anyway), then you can override the setFrame method to whatever width you need. No need to redesign your app or do anything tricky.

Resources