Add a Webview in a tableViewCell - ios

I put a WebView in a tableViewCell and I use it in a tableView. Until now everything is working fine. But I was using a fixed size for the WebViewCell. Now I want to show all the content of the WebView. I changed my code to this one in the MyTbaleView class
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if let cell = modelCollection[collection.identifier] {
if let _ = cell as? TiteCell{
return 200
} else if let _ = cell as? myWebViewCell{
return UITableViewAutomaticDimension
}
}
func tableView(tableView: UITableView, EstimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 1000
}
and this is the xib file
myWebView Xib file
With this code the WebView become really small, a few millimeters. I tried to add this code I found in the forum to MyWebViewCell class
func webViewDidFinishLoad(webView : UIWebView) {
var frame = myWebView.frame
frame.size.height = 1
myWebView.frame = frame
let fittingSize = myWebView.sizeThatFits(CGSize(width: 0, height: 0))
frame.size = fittingSize
myWebView.frame = frame
myWebView.scrollView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0);
}
Now the webview is a little bit bigger, 1 centimeter and the width is larger than the ipad width.

As #Ramaraj T said, its a very costlier process and I to approve that you should change your design. But if you still want to continue you can do something like this.
Create a HeightConstraint for WebView in your custom cell.
// Code in cell
var webViewHeightConstraint: NSLayoutConstarint!
// Code in controller
// code cellForIndexPath
webView.tag = indexPath.row(or any unique value which can be used to calculate indexPath)
func webViewDidFinishLoad(webView : UIWebView) {
var frame = myWebView.frame
frame.size.height = 1
myWebView.frame = frame
let fittingSize = myWebView.sizeThatFits(CGSize(width: 0, height: 0))
frame.size = fittingSize
myWebView.frame = frame
myWebView.scrollView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0);
let height = myWebView.contentSize.height
let section = webView.tag
let indexPath = NSIndexPath(forRow: 0, inSection: section)
let cell = tableView.dequeCell(atIndexPath: indexPath)//
cell.webViewHeightConstraint.constant = height
cell.layoutConstraintsIfNeeded()
tableView.beginUpdates()
tableView.endUpdates()
}

Related

UITableView Custom Cell Overlapping in ios10 with Swift3

I am making UITableView custom cell because height also changed per cell.
This is my code for initialize cell and after that i want to add UITextView as Subview.
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
{
let dictAd = self.arrAllQuestions[indexPath.row] as! NSDictionary
let fontNew = UIFont(name: "AvenirNext-Regular", size: 19.0)
let strA = "\(indexPath.row+1). \(dictAd.object(forKey: "Title")!)"
let heightTitle = self.heightForView(text: strA, font: fontNew!, width: tableView.frame.size.width-16)
return heightTitle+5;
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let dictAd = self.arrAllQuestions[indexPath.row] as! NSDictionary
let cell = tableView.dequeueReusableCell(withIdentifier: "quesionCell", for: indexPath) as! QuestionCell
let fontNew = UIFont(name: "AvenirNext-Regular", size: 19.0)
let strA = "\(indexPath.row+1). \(dictAd.object(forKey: "Title")!)"
cell.lblName.font = fontNew
cell.lblName.text = strA
cell.lblName.numberOfLines = 0
let heightTitle = self.heightForView(text: strA, font: fontNew!, width: tableView.frame.size.width-16)
var frame = cell.lblName.frame as CGRect
frame.size.height = heightTitle; //you need to adjust this value
cell.lblName.frame = frame;
let txtView = AnimatableTextView(frame: CGRect(x: 8, y: cell.lblName.frame.origin.y+cell.lblName.frame.size.height+5, width: tableView.frame.size.width-16, height: 25))
txtView.borderWidth = 1.0
txtView.borderColor = UIColor.lightGray
txtView.cornerRadius = 4.0
cell.contentView.addSubview(txtView)
return cell
}
You can see output below.
It seems that the height calculation in your heightForRowAtIndexPath is not proper. Consider using self-sizing cells using UITableViewAutomaticDimension and Autolayout to solve your issues. Here is a great tutorial that can help you to get started. One more suggestion if you are using UITextView or subclass of the same in a cell and you want it to take the height of the content set its scrollable property to false.
Declare this in the viewDidLoad
Hope this will help you
tableView.estimatedRowHeight = 44

updating tableViewCell's height and moving tableView at the same time

i'm moving up my tableView (via setting contentOffset) and changing a specific (row no. 1 here ) cell's height at the same time , thats why i'm getting a shaky animation and also tableView restores itself to the default position (which is not desired behavior )
my code :
func keyboardWillShow(notification:NSNotification) {
let userInfo:NSDictionary = notification.userInfo!
let keyboardFrame:NSValue = userInfo.valueForKey(UIKeyboardFrameEndUserInfoKey) as! NSValue
let keyboardRectangle = keyboardFrame.CGRectValue()
let duration = (notification.userInfo![UIKeyboardAnimationDurationUserInfoKey] as! Double)
keyboardHeight = keyboardRectangle.height
accurateheight = accurateheight - (keyboardHeight + inputToolbar.frame.size.height) // accurateHeight is the desired CGFloat value that i want
self.tableView.beginUpdates()
self.tableView.endUpdates()
let frame = tableView.rectForRowAtIndexPath(NSIndexPath(forRow: 0, inSection: 0))
defaultRowHeight = frame.height
let cgpoint = CGPointMake(0, (CGFloat(-64 + defaultRowHeight)))
self.tableView.setContentOffset(cgpoint, animated: true) // here i'm moving up my tableView
}
in my heightForRowAtIndexPath:
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if indexPath.row == 0 {
return 44
}else {
return accurate height // cell's height which i'm changing
}
}
i also tried to update only specific row (the ones that i'm changing height of) like this :
var indexPath = NSIndexPath(forRow: 1, inSection: 0)
self.tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Top)
but the above didnt worked also sometimes my keyboard was not popping up.
how can i move (scroll up) my tableView and change a cell's height at the same time ?? any clue ?

Table view overlapping cells swift2

I have a tableview in my app. it's on the detail view of my split view. Whenever i populate my tableview with UItextviews, the cells seem to overlap each other. this is not what i want. Does anyone have any suggestions to fix this?
here's my vc code:
import UIKit
class DetailViewController: UIViewController, UITableViewDelegate, UITextViewDelegate {
#IBOutlet weak var messagesTableView: UITableView!
var initVar = 0
var row: Int?
var firstLoad = true
var detailItem = {}
var convo = ["hello", "hellohello", "hellohellohellohello", "hellohellohellohellohellohellohellohello", "hellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohello", "hellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohello", "hellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohello", "hellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohello", "hellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohello", "hellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohello"]
override func viewDidLoad() {
super.viewDidLoad()
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem()
print("hello")
messagesTableView.separatorColor = UIColor.whiteColor()
print(row)
messagesTableView.reloadData()
print(firstLoad)
let numberOfSections = messagesTableView.numberOfSections
let numberOfRows = messagesTableView.numberOfRowsInSection(numberOfSections-1)
if numberOfRows > 0 {
print(numberOfSections)
let indexPath = NSIndexPath(forRow: numberOfRows-1, inSection: (numberOfSections-1))
messagesTableView.scrollToRowAtIndexPath(indexPath, atScrollPosition: UITableViewScrollPosition.Bottom, animated: false)
}
messagesTableView.reloadData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return convo.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("detailCell", forIndexPath: indexPath)
var textView = UITextView()
textView.delegate = self
textView.editable = false
textView.text = convo[indexPath.row]
textView.textAlignment = .Center
textView.sizeToFit()
if indexPath.row % 2 == 0 {
textView = UITextView(frame: CGRect(x: cell.frame.width * (2/5), y: 4, width: cell.frame.width * (3/5), height: cell.frame.height))
textView.backgroundColor = UIColor.blueColor()
} else {
textView = UITextView(frame: CGRect(x: 0, y: 4, width: cell.frame.width * (3/5), height: cell.frame.height))
textView.backgroundColor = UIColor.greenColor()
}
textView.textColor = UIColor.whiteColor()
textView.delegate = self
textView.editable = false
textView.text = convo[indexPath.row]
textView.textAlignment = .Center
textView.sizeToFit()
tableView.rowHeight = textView.frame.height + 7
cell.addSubview(textView)
return cell
}
}
The cells are being re-used in a UITableView.
You are adding TextView's on it, so you are keep on adding TextView's every time it's being re-used.
Your code should be modified so that the TextView is only added IF it's not already present on the cell.
The easiest way, is to add a TAG (unique) to the TextView, then if the View from Tag is nil (AKA not present) add it, else get it from the ViewFromTag
As for an example, although I'm still using obj-c and with that in mind, my code might have an error, here is the code:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("detailCell", forIndexPath: indexPath)
var textView: UITextView
if (cell.viewWithTag(10000) != nil) {
textView = (cell.viewWithTag(10000) as? UITextView)!
}
else {
textView = UITextView()
textView.tag = 10000
textView.delegate = self
textView.editable = false
textView.textAlignment = .Center
cell.addSubview(textView)
}
textView.text = convo[indexPath.row]
if indexPath.row % 2 == 0 {
textView.frame = CGRectMake(cell.frame.width * (2/5) , 4, cell.frame.width * (3/5), cell.frame.height)
textView.backgroundColor = UIColor.blueColor()
} else {
textView.frame = CGRectMake(0 , 4, cell.frame.width * (3/5), cell.frame.height)
textView.backgroundColor = UIColor.greenColor()
}
textView.sizeToFit()
tableView.rowHeight = textView.frame.height + 7
return cell
}
To explain the code, as you didn't understood:
The UITableView is re-using the cells.
This means, whenever a cell is moved outside the visible part of the screen, it is been re-used (as it is) and is displayed as a new cell for the TableView.
The reason for this, is memory efficiency.
So imagine a UITableView with 1000 row. If for each row a new cell was created, we will have to allocate memory for each cell.
But in a typical application usually only a few cells are visible each time (usually less than 10).
So the trick here is that we only need to create that many cells.
Once a cell gets out of sight, it is been served as a new cell for the TableView.
So if you add a UITextView every time the tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell delegate method is called, and the cell is been re-used, you end up adding UITextViews to a cell that already has one. So you end up with the problem you showed.
The code I provided does two things differently:
It does assign a unique Tag number on the UITextView when it's added to the cell (textView.tag = 10000)
It does check if a UITextView has already been added to the cell and if it has been added we use that UITextView and we don't add it again (if (cell.viewWithTag(10000) != nil))

UICollectionView : Don't reload supplementary views

I am using a collection view to display datas fetched from the web service. I also have a supplementary view (header), which contains a UIImageView and a label. The UIImageView animates to show an array of images. The problem arises when I scroll the view. When the header is hidden and then scrolled up showing it, the app freezes briefly.
func collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView {
let headerView = categoryView.dequeueReusableSupplementaryViewOfKind(UICollectionElementKindSectionHeader, withReuseIdentifier: "bannerHeader", forIndexPath: indexPath) as! HeaderBanner
print("Got into header")
print("THE NUMBER OF AD ITEMS IS: \(self.adItems.count)")
var sliderImages = [UIImage]()
var imageAddressArray = [String]()
if(self.adItems.count>0) {
print("AD ITEMS IS GREATER THAN 0")
for i in 0..<self.adItems.count {
imageAddressArray.append(URLEncoder.encodeURL(self.adItems[i].filePath!))
}
dispatch_async(dispatch_get_main_queue(), {
AdsImageDataFetch.fetchImageData(imageAddressArray) { result -> () in
sliderImages = result
self.animateImageView(headerView.bannerImage, images: sliderImages, label: headerView.bannerLabel)
}
})
}
return headerView
}
I think I have done this correctly. So, I was wondering if there is any way to not load the header when the scrolling takes place. New to iOS and Swift.
Since I couldn't find a solution I used a floating header view instead so that it wouldn't get refreshed every time on scroll. For other's who want to use the floating header view in Swift 2.0. Here is the code:
class StickyHeaderFlowLayout: UICollectionViewFlowLayout {
override func shouldInvalidateLayoutForBoundsChange(newBounds: CGRect) -> Bool {
// Return true so we're asked for layout attributes as the content is scrolled
return true
}
override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
// Get the layout attributes for a standard UICollectionViewFlowLayout
var elementsLayoutAttributes = super.layoutAttributesForElementsInRect(rect)
if elementsLayoutAttributes == nil {
return nil
}
// Define a struct we can use to store optional layout attributes in a dictionary
struct HeaderAttributes {
var layoutAttributes: UICollectionViewLayoutAttributes?
}
var visibleSectionHeaderLayoutAttributes = [Int : HeaderAttributes]()
// Loop through the layout attributes we have
for (index, layoutAttributes) in (elementsLayoutAttributes!).enumerate() {
let section = layoutAttributes.indexPath.section
switch layoutAttributes.representedElementCategory {
case .SupplementaryView:
// If this is a set of layout attributes for a section header, replace them with modified attributes
if layoutAttributes.representedElementKind == UICollectionElementKindSectionHeader {
let newLayoutAttributes = layoutAttributesForSupplementaryViewOfKind(UICollectionElementKindSectionHeader, atIndexPath: layoutAttributes.indexPath)
elementsLayoutAttributes![index] = newLayoutAttributes!
// Store the layout attributes in the dictionary so we know they've been dealt with
visibleSectionHeaderLayoutAttributes[section] = HeaderAttributes(layoutAttributes: newLayoutAttributes)
}
case .Cell:
// Check if this is a cell for a section we've not dealt with yet
if visibleSectionHeaderLayoutAttributes[section] == nil {
// Stored a struct for this cell's section so we can can fill it out later if needed
visibleSectionHeaderLayoutAttributes[section] = HeaderAttributes(layoutAttributes: nil)
}
case .DecorationView:
break
}
}
// Loop through the sections we've found
for (section, headerAttributes) in visibleSectionHeaderLayoutAttributes {
// If the header for this section hasn't been set up, do it now
if headerAttributes.layoutAttributes == nil {
let newAttributes = layoutAttributesForSupplementaryViewOfKind(UICollectionElementKindSectionHeader, atIndexPath: NSIndexPath(forItem: 0, inSection: section))
elementsLayoutAttributes!.append(newAttributes!)
}
}
return elementsLayoutAttributes
}
override func layoutAttributesForSupplementaryViewOfKind(elementKind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionViewLayoutAttributes? {
// Get the layout attributes for a standard flow layout
let attributes = super.layoutAttributesForSupplementaryViewOfKind(elementKind, atIndexPath: indexPath)
// If this is a header, we should tweak it's attributes
if elementKind == UICollectionElementKindSectionHeader {
if let fullSectionFrame = frameForSection(indexPath.section) {
let minimumY = max(collectionView!.contentOffset.y + collectionView!.contentInset.top, fullSectionFrame.origin.y)
let maximumY = CGRectGetMaxY(fullSectionFrame) - headerReferenceSize.height - collectionView!.contentInset.bottom
attributes!.frame = CGRect(x: 0, y: min(minimumY, maximumY), width: collectionView!.bounds.size.width, height: headerReferenceSize.height)
attributes!.zIndex = 1
}
}
return attributes
}
// MARK: Private helper methods
private func frameForSection(section: Int) -> CGRect? {
// Sanity check
let numberOfItems = collectionView!.numberOfItemsInSection(section)
if numberOfItems == 0 {
return nil
}
// Get the index paths for the first and last cell in the section
let firstIndexPath = NSIndexPath(forRow: 0, inSection: section)
let lastIndexPath = numberOfItems == 0 ? firstIndexPath : NSIndexPath(forRow: numberOfItems - 1, inSection: section)
// Work out the top of the first cell and bottom of the last cell
let firstCellTop = layoutAttributesForItemAtIndexPath(firstIndexPath)!.frame.origin.y
let lastCellBottom = CGRectGetMaxY(layoutAttributesForItemAtIndexPath(lastIndexPath)!.frame)
// Build the frame for the section
var frame = CGRectZero
frame.size.width = collectionView!.bounds.size.width
frame.origin.y = firstCellTop
frame.size.height = lastCellBottom - firstCellTop
// Increase the frame to allow space for the header
frame.origin.y -= headerReferenceSize.height
frame.size.height += headerReferenceSize.height
// Increase the frame to allow space for an section insets
frame.origin.y -= sectionInset.top
frame.size.height += sectionInset.top
frame.size.height += sectionInset.bottom
return frame
}
}

UICollectionViewFlowLayout Subclass causes some cells to not appear

I have a vertically scrolling UICollectionView that uses a subclass of UICollectionViewFlowLayout to try and eliminate inter-item spacing. This would result in something that looks similar to a UITableView, but I need the CollectionView for other purposes. There is a problem in my implementation of the FlowLayout subclass that causes cells to disappear when scrolling fast. Here is the code for my FlowLayout subclass:
EDIT: See Comments For Update
class ListLayout: UICollectionViewFlowLayout {
override func layoutAttributesForElementsInRect(rect: CGRect) -> [AnyObject]? {
if var answer = super.layoutAttributesForElementsInRect(rect) {
for attr in (answer as [UICollectionViewLayoutAttributes]) {
let ip = attr.indexPath
attr.frame = self.layoutAttributesForItemAtIndexPath(ip).frame
}
return answer;
}
return nil
}
override func layoutAttributesForItemAtIndexPath(indexPath: NSIndexPath) -> UICollectionViewLayoutAttributes! {
let currentItemAtts = super.layoutAttributesForItemAtIndexPath(indexPath) as UICollectionViewLayoutAttributes
if indexPath.item == 0 {
var frame = currentItemAtts.frame
frame.origin.y = 0
currentItemAtts.frame = frame
return currentItemAtts
}
let prevIP = NSIndexPath(forItem: indexPath.item - 1, inSection: indexPath.section)
let prevFrame = self.layoutAttributesForItemAtIndexPath(prevIP).frame
let prevFrameTopPoint = prevFrame.origin.y + prevFrame.size.height
var frame = currentItemAtts.frame
frame.origin.y = prevFrameTopPoint
currentItemAtts.frame = frame
return currentItemAtts
}
}
One other thing to note: My cells are variable height. Their height is set by overriding preferredLayoutAttributesFittingAttributes in the subclass of the custom cell:
override func preferredLayoutAttributesFittingAttributes(layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes! {
let attr: UICollectionViewLayoutAttributes = layoutAttributes.copy() as UICollectionViewLayoutAttributes
attr.frame.size = CGSizeMake(self.frame.size.width, myHeight)
return attr
}
And I set the layout's estimated size on initialization:
flowLayout.estimatedItemSize = CGSize(width: UIScreen.mainScreen().bounds.size.width, height: 60)
Here is a GIF that demonstrates this problem:
Does anybody have an idea as to what's going on? Your help is much appreciated.
Thanks!

Resources