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.
Related
Hi im newer to IOS dev and Im more familiar with web dev. I am running into an issue where my cells are originally rendered how I would expect them to, but after scrolling to the bottom and scrolling back to the top of my screen I noticed that the cells begin to overlap each other. After reading other stack overflow posts I am starting to think that it has to do with how I create the cells. I am attempting to create multiple labels in a cell with a for loop similar to how someone would with PHP for web dev and im wondering if this is the proper way to do it/ is this a possible cause.
for i in 0...bars.count-1 {
var barLabelView:UIView = UIView()
var barLabel:UILabel = UILabel()
barLabelView.frame = CGRect(x: 20, y: CGFloat(20 + (70 * i)), width: barView.frame.width-40, height: 50)
barLabelView.backgroundColor = UIColor(displayP3Red: 27/255.0, green: 99/255.0, blue: 222/255.0, alpha: 1)
barLabelView.layer.cornerRadius = 10
barLabel.frame = CGRect(x: 0, y: 0, width: barLabelView.frame.width-10, height: 50)
barLabel.text = bars[i]
barLabel.textAlignment = .center
barLabel.numberOfLines = 0
barLabel.font = UIFont(name: "Chalkboard SE", size: 15)
barView.addSubview(barLabelView)
barLabelView.addSubview(barLabel)
}
Also heres how i set the height of the cell:
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
let crawlNameText = self.posts[indexPath.row].text
let size = CGSize(width: view.frame.width - 40, height: 1000)
let options = NSStringDrawingOptions.usesFontLeading.union(.usesLineFragmentOrigin)
let estimatedCrawlNameFrame = NSString(string: crawlNameText).boundingRect(with: size, options: options, attributes: [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 16)], context: nil)
let bars = posts[indexPath.row].bars.components(separatedBy: ", ")
let buds = posts[indexPath.row].buds.components(separatedBy: ", ")
if (self.posts[indexPath.row].type == "crawl") {
var cellHeightP1 = CGFloat(estimatedCrawlNameFrame.height + 110 + 20 + 20 + 20 + 40 + 60)
var cellHeightP2:CGFloat = CGFloat( 80 * bars.count)
var cellHeightP3:CGFloat = CGFloat( 80 * buds.count)
var cellHeight = cellHeightP1 + cellHeightP2 + cellHeightP3
return cellHeight
} else {
return estimatedCrawlNameFrame.width + 110 + 20
}
}
and here is my cellForRowAt function
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = nfTV.dequeueReusableCell(withIdentifier: "cellId", for: indexPath) as! newsFeedCell
// Im guessing I would set the cell to nill here to clear the buttons I made
cell.setPost(post: posts[indexPath.row])
if (cell.profilePic != nil){
cell.profilePic.layer.cornerRadius = cell.profilePic.bounds.height / 2
cell.profilePic.clipsToBounds = true
}
cell.backgroundColor = UIColor.lightGray
print("made it here")
print(indexPath.row)
cell.frame = CGRect(x: 0, y: 0, width: cell.frame.width, height: cell.frame.height)
return cell
}
I read that I might have to clear my cell before reusing it but im not sure how to set a cell to nil. I am also wondering if using a collection view would work better but I dont know the difference between that and a tableview.
Any help or suggestions is appreciated, thanks.
I answered my own question but I guess Ill leave this up in case it helps anyone in the future
I added the following lines of code at my comment in the cellForRowAt function to remove the buttons
for v in cell.barView.subviews{
v.removeFromSuperview()
}
for v in cell.budsView.subviews{
v.removeFromSuperview()
}
The issue was caused by some cells having more buttons than others so cells with less buttons were rendered with more buttons than necessary making it appear like it was overlap but in reality it was just recycled buttons.
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
}
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()
}
I'm building a chat application where each message are inserted into a row inside a table. Each row contains an avatar and a message. I want to set the width and height of the UITextArea as per the length of the text to put inside.
Below is the code I've used. But here, both height and width are constant (200x50)
PS: I'm a newbie to Swift and ios and I'm using Swift 3. Every code I got after searching is in objective-c or swift 2
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = Bundle.main.loadNibNamed("ChatBox1", owner: self, options: nil)?.first as! ChatBox1
let myTextField: UITextView = UITextView(frame: CGRect(x: 30, y: 20, width: 200.00, height: 50.00));
cell.addSubview(myTextField)
myTextField.isScrollEnabled = false
myTextField.isUserInteractionEnabled = false
myTextField.backgroundColor = UIColor.lightGray
myTextField.text = "Lorem Ipsum is simply dummy text of the printing and typesetting industry."
myTextField.layer.cornerRadius = 5
print(myTextField.intrinsicContentSize)
let image = UIImage(named: "agent")
let imageView = UIImageView(image: image)
imageView.frame = CGRect(x: 0, y: 0, width: 40, height: 40)
cell.addSubview(imageView)
return cell
}
This two methods are done to set Height and width of textview according to the text on chat.
You can use these. (swift 2.3)
var screenrect = UIScreen.mainScreen().bounds
func GET_WIDTH(textView : UITextView , text : String)-> CGFloat
{
textView.text = text;
let size = textView.bounds.size
let newSizeWidth = textView.sizeThatFits(CGSize(width: CGFloat.max, height: size.height))
// Resize the cell only when cell's size is changed
if size.width != newSizeWidth.width
{
if( newSizeWidth.width > screenrect.width-120 )
{
textView.layer.frame.size.width = screenrect.width-120;
}
else if ( newSizeWidth.width < 40 )
{
textView.layer.frame.size.width = 40
}
else
{
textView.layer.frame.size.width = newSizeWidth.width;
}
}
return textView.layer.frame.size.width + 40;
}
func GET_HEIGHT(textView : UITextView , text : String)-> CGFloat
{
textView.text = text;
let size2 = textView.bounds.size
let newSize = textView.sizeThatFits(CGSize(width: size2.width, height: CGFloat.max))
if size2.height != newSize.height
{
if( newSize.height < 40 )
{
textView.layer.frame.size.height = 40
}
else
{
textView.layer.frame.size.height = newSize.height
}
}
return textView.layer.frame.size.height + 15;
}
You have to call the functions of the textview like this to set height and width.
let textViewRight=UITextView(frame: CGRectMake(0, 4, 40, 40));
textViewRight.layer.frame.size.width = GET_WIDTH(textViewRight, text: currentObject.chat_userMessage)
textViewRight.layer.frame.size.height = GET_HEIGHT(textViewRight, text: currentObject.chat_userMessage)
Try this code:
Answer 1:
func tableView(tableView:UITableView!, numberOfRowsInSection section: Int) -> Int {
yourTableViewName.estimatedRowHeight = 44.0 // Standard tableViewCell size
yourTableViewName.rowHeight = UITableViewAutomaticDimension
return yourArrayName.count }
And also put this code inside your Cell for incase...
yourCell.sizeToFit()
Answer 2:
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
override func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
in viewDidLoad Add this
tableView.estimatedRowHeight = 44.0
tableView.rowHeight = UITableViewAutomaticDimension
Just add these two lines and your problem will b solved
Take textView height constraint and set it equal to the content size of the textView.
Alternatively, if you use autolayout you can set the content hugging and compression property to 1000. It should expand your cell according to the content.
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell: MaterialCollectionViewCell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as! MaterialCollectionViewCell
let item = items()[indexPath.item]
//print(cell.contentView.subviews)
for view in cell.contentView.subviews{
view.removeFromSuperview()
}
//print(cell.contentView.subviews)
let colors = [UIColor.grayColor(), UIColor.darkGrayColor()]
cell.backgroundColor = colors[indexPath.item % 2]
let showTitle: UILabel = UILabel(frame: CGRect(x: 0, y: 0, width: cell.frame.width, height: cell.frame.height))
showTitle.text = item.title
showTitle.textAlignment = .Center
showTitle.font = UIFont(name: "HelveticaNeue", size: 25.0)
showTitle.textColor = MaterialColor.white
showTitle.center = cell.center
showTitle.hidden = true
//print(showTitle.text)
cell.contentView.addSubview(showTitle)
let imageView: UIImageView = UIImageView(frame: CGRect(x: 0, y: 0, width: cell.frame.width, height: cell.frame.height))
imageView.kf_setImageWithURL(NSURL(string: item.banner)!,
placeholderImage: nil,
optionsInfo: nil,
progressBlock: { (receivedSize, totalSize) -> () in
//print("Download Progress: \(receivedSize)/\(totalSize)")
},
completionHandler: { (image, error, cacheType, imageURL) -> () in
//print("Downloaded and set!")
if (image != nil) {
showTitle.removeFromSuperview()
}
else {
showTitle.hidden = false
}
}
)
cell.contentView.addSubview(imageView)
//print(cell.contentView.subviews)
return cell
}
this is the code I am using to display data into show title label. For the moment imageView doesn't ever show
issue with mine is the complete data is not displayed instead a part of it
current output
Should have shown these in the same order
Game of Thrones
Orange Is the New Black
Marco Polo (2014)
Person of Interest
Dark Matter
I think the problem is that the UILabel view and the UIImageView are overlapped 。 You should shrink the size of UILabel, and put the UIImageView behind the UILabel.