how to blur only the part of uitableview getting scrolled - ios

I have to implement a menu just like the "example.gif" in the following library link
i'm using this same library to blur the background.
i've used contentInset so that the first three rows show up from the bottom.
Now, my problem is when i start scrolling the entire screen is blurred, whereas i want to blur the part of screen where uitableviewcells are getting scrolled. (Ultimately, the entire screen will be blurred as soon as the first cell reaches the top).
how can i achieve this. If there is any workaround without using the library, it is also welcome. Here is the code--
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, UIScrollViewDelegate{
#IBOutlet weak var table: UITableView!
var blurView: DKLiveBlurView!
var unsortedCountryArray:[String] = []
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let topInset = self.view.frame.height - 120
// Array to display.
let countryArray = NSLocale.ISOCountryCodes()
for countryCode in countryArray {
let displayNameString = NSLocale.currentLocale().displayNameForKey(NSLocaleCountryCode, value: countryCode)
if displayNameString != nil {
unsortedCountryArray.append(displayNameString!)
}
}
// =============setting background==============
// self.bkgView = UIImageView(frame: self.view.bounds)
// self.bkgView.image = UIImage(named: "bg1")
// self.bkgView.contentMode = .ScaleAspectFill
// self.view.addSubview(self.bkgView)
// self.blurredBkgView = UIImageView(frame: self.view.bounds)
// self.blurredBkgView.image = UIImage(named: "bg1")
// self.blurredBkgView.contentMode = .ScaleAspectFill
// self.view.addSubview(blurredBkgView)
// self.blurredBkgView.alpha = 0.0
//
// blurEffect = UIBlurEffect(style: .Light)
// visualEffectView = UIVisualEffectView(effect: blurEffect)
// visualEffectView.frame = self.blurredBkgView.bounds
// self.visualEffectView.alpha = 0.0
// self.view.addSubview(self.visualEffectView)
self.table.backgroundColor = UIColor.clearColor()
self.table.separatorColor = UIColor.clearColor()
self.table.contentInset = UIEdgeInsetsMake(topInset, 0, 0, 0)
self.table.rowHeight = 40
print("view bounds: \(self.view.bounds)\n and table bounds: \(self.table.bounds)")
self.blurView = DKLiveBlurView(frame: self.table.bounds)
self.blurView.originalImage = UIImage(named: "bg1")
self.blurView.scrollView = table
self.blurView.setBlurLevel(6.0)
self.blurView.isGlassEffectOn = true
self.table.backgroundView = self.blurView
}
func scrollViewDidScroll(scrollView: UIScrollView) {
// let height = CGFloat(scrollView.bounds.size.height)
// let position = max(scrollView.contentOffset.y, 0.0)
// let percent = min(position / height, 1.0)
// self.blurredBkgView.alpha = percent;
// print("scrollview bounds: \(scrollView.bounds)")
}
func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
// cell.backgroundView = self.blurView
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return unsortedCountryArray.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell")!
cell.textLabel?.text = unsortedCountryArray[indexPath.row]
cell.textLabel?.textColor = UIColor.blueColor()
cell.backgroundColor = UIColor.clearColor()
cell.selectionStyle = .None
return cell
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
This much code is blurring while scrolling.

To account for your content inset you need to change the frame you provide to your blurView
Eg
let contentInset = CGFloat(/*Your content inset*/)
let blurFrame = CGRect(x: 0, y: contentInset, width: tableView.frame.width, height: tableView.frame.height - contentInset)
self.blurView = DKLiveBlurView(frame: blurFrame)
EDIT: Old answer
You seem to be using bounds rather than frame for your DKLiveBlurView. This will cause you blur view to start from the top left of the screen (the origin of your view's frame)
Try:
self.blurView = DKLiveBlurView(frame: self.table.frame)
Rather than
self.blurView = DKLiveBlurView(frame: self.table.bounds)

Related

How do I make new image views movable?

I have been trying to piece together a project that contains a collection view that scrolls on the side, and when a cell is tapped it will add a new image view to the scene. I would like this new image to be draggable.
My code currently shows the collection view and when tapped adds a new image (which I call stickers).
I haven't quite figured out how to make it place the correct sticker yet, but my first goal is to make sure what appears can be moved.
import UIKit
class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
#IBOutlet weak var collectionView: UICollectionView!
let stickers: [UIImage] = [
UIImage(named: "cow")!,
UIImage(named: "chicken")!,
UIImage(named: "pig")!,
UIImage(named: "cow")!,
UIImage(named: "chicken")!,
UIImage(named: "pig")!,
UIImage(named: "cow")!,
UIImage(named: "chicken")!,
UIImage(named: "pig")!,
]
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return stickers.count
}
var activeSticker = UIImage(named: "cow")
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CollectionViewCell
cell.stickerImage.image = stickers[indexPath.item]
cell.backgroundColor = UIColor(white: 1, alpha: 0.9)
cell.translatesAutoresizingMaskIntoConstraints = false
cell.contentMode = .scaleAspectFill
cell.clipsToBounds = true
cell.layer.cornerRadius = 7
let addSticker = UIButton(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
addSticker.addTarget(self, action: #selector(addStickerTapped), for: UIControl.Event.touchUpInside)
activeSticker = cell.stickerImage.image
cell.addSubview(addSticker)
return cell
}
#IBAction func addStickerTapped() -> Void {
print("Hello Sticker Button")
let image = activeSticker //UIImage(named: imageName)
let imageView = UIImageView(image: image!)
imageView.frame = CGRect(x: 100, y: 100, width: 100, height: 200)
imageView.contentMode = .scaleAspectFit
imageView.isUserInteractionEnabled = true
self.view.addSubview(imageView)
//Imageview on Top of View
self.view.bringSubviewToFront(imageView)
}
}
To move the imageView around inside self.view you can use UIPanGestureRecognizer. Add the gesture recognizer, for example, in viewDidLoad:
class ViewController: UIViewController {
var selectedImageView: UIImageView?
override func viewDidLoad() {
super.viewDidLoad()
addPanGestureRecognizer()
}
func addPanGestureRecognizer() {
let pan = UIPanGestureRecognizer(target: self, action: #selector(moveImageView(_:)))
// set up and optimize pan gesture options here if you need to
self.view.addGestureRecognizer(pan)
}
#objc func moveImageView(_ sender: UIPanGestureRecognizer) {
// assign one of your image views to selectedImageView to ensure you only move one image view at a time
// for example in func addStickerTapped() you could assign selectedImageView = imageView
guard let selectedImageView = selectedImageView else {
return
}
switch sender.state {
case .changed, .ended:
selectedImageView.center = selectedImageView.center.offset(by: sender.translation(in: self.view))
sender.setTranslation(.zero, in: self.view)
default:
break
}
}
}
extension CGPoint {
func offset(by point: CGPoint) -> CGPoint {
return CGPoint(x: self.x + point.x, y: self.y + point.y)
}
}

When Should I Recalculate A UITableView Footer's Size?

I would like to recalculate the height of a table view's footer based upon the table view's changing content size. When the table has zero rows the height of the footer will be at its maximum. As rows are added to the table the footer's height will be reduced until it reaches a minimum. What I am doing is using the footer to fill up the empty space that appears at the bottom of the table when there are zero or few rows. In addition to rows being added it is possible for the content size to change because the height (content) of an existing row has been changed.
Supposing that I have a view controller whose main view contains two subviews: a button and a table view. Clicking the button results in the data store being modified and the table's reloadData method being called. When/Where would I assign a new value to the table's tableFooterView.bounds.size.height?
I should also point out that I am using UITableViewAutomaticDimension. If, in the table's data source delegate method cellForRowAt, I print the cell heights I get:
Upper table cell height = 21.0
Upper table cell height = 21.0
Upper table cell height = 21.0
Upper table cell height = 21.0
Upper table cell height = 44.0
All 21 except for the last one, the new one. This must be due to the automatic dimensioning not yet having been applied.
Update:
I have tentatively arrived at the following solution (many thanks to all of the folks on this thread for the biggest part of the solution). I am tentative because the solution involves calling reloadData twice in order to deal with an issue with the contentSize. See this GitHub project for a demo of the contentSize issue.
class TableView: UITableView {
override func reloadData() {
execute() { super.reloadData() }
}
override func reloadRows(at indexPaths: [IndexPath], with animation: UITableView.RowAnimation) {
execute() { super.reloadRows(at: indexPaths, with: animation) }
}
private func execute(reload: #escaping () -> Void) {
CATransaction.begin()
CATransaction.setCompletionBlock() {
if self.adjustFooter() {
reload() // Cause the contentSize to update (see GitHub project)
self.layoutIfNeeded()
}
}
reload()
CATransaction.commit()
}
// Return true(false) if the footer was(was not) adjusted
func adjustFooter() -> Bool {
guard let currentFrame = tableFooterView?.frame else { return false }
let newHeight = calcFooterHeight()
let adjustmentNeeded = newHeight != currentFrame.height
if adjustmentNeeded {
tableFooterView?.frame = CGRect(x: currentFrame.minX, y: currentFrame.minY, width: currentFrame.width, height: newHeight)
}
return adjustmentNeeded
}
private let minFooterHeight: CGFloat = 44
private func calcFooterHeight() -> CGFloat {
guard let footerView = tableFooterView else { return 0 }
let spaceTaken = contentSize.height - footerView.bounds.height
let spaceAvailable = bounds.height - spaceTaken
return spaceAvailable > minFooterHeight ? spaceAvailable : minFooterHeight
}
}
UITableViewDelegate has method tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat which we can use to specifiy height of section footers. This method fires when we call reloadData() for table view or when screen orientation was changed, etc.
So you can implement this method to calculate a new height of the footer:
override func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
guard section == 0 else { return 0.0 } // assume there is only one section in the table
var cellsHeight: CGFloat = 0.0
let rows = self.tableView(tableView, numberOfRowsInSection: section)
for row in 0..<rows
{
let indexPath = IndexPath(item: row, section: section)
cellsHeight += self.tableView(tableView, heightForRowAt: indexPath)
}
let headerHeight: CGFloat = tableView.tableHeaderView?.frame.height ?? 0.0
let footerHeight = view.frame.height - headerHeight - cellsHeight
return footerHeight
}
I arrived at the following solution. Many thanks to all of the folks on this thread for the biggest part of the solution. The TableViewController.TableView class provides the desired functionality. The remainder of the code fleshes out a complete example.
//
// TableViewController.swift
// Tables
//
// Created by Robert Vaessen on 11/6/18.
// Copyright © 2018 Robert Vaessen. All rights reserved.
//
// Note: Add the following to AppDelegate:
//
// func application(_ application: UIApplication,
// didFinishLaunchingWithOptions launchOptions:
// [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// window = UIWindow(frame: UIScreen.main.bounds)
// window?.makeKeyAndVisible()
// window?.rootViewController = TableViewController()
// return true
// }
import UIKit
class TableViewController: UIViewController {
class TableView : UITableView {
override func reloadData() {
execute() { super.reloadData() }
}
override func reloadRows(at indexPaths: [IndexPath], with animation: UITableView.RowAnimation) {
execute() { super.reloadRows(at: indexPaths, with: animation) }
}
private func execute(reload: #escaping () -> Void) {
CATransaction.begin()
CATransaction.setCompletionBlock() {
print("Reload completed")
_ = self.adjustFooter()
}
print("\nReload begun")
reload()
CATransaction.commit()
}
private func adjustFooter() -> Bool {
guard let footerView = tableFooterView else { return false }
func calcFooterHeight() -> CGFloat {
var heightUsed = tableHeaderView?.bounds.height ?? 0
for cell in visibleCells { heightUsed += cell.bounds.height }
let heightRemaining = bounds.height - heightUsed
let minHeight: CGFloat = 44
return heightRemaining > minHeight ? heightRemaining : minHeight
}
let newHeight = calcFooterHeight()
guard newHeight != footerView.bounds.height else { return false }
// Keep the origin where it is, i.e. tweaking just the height expands the frame about its center
let currentFrame = footerView.frame
footerView.frame = CGRect(x: currentFrame.origin.x, y: currentFrame.origin.y, width: currentFrame.width, height: newHeight)
return true
}
}
class FooterView : UIView {
override func draw(_ rect: CGRect) {
print("Drawing footer")
super.draw(rect)
}
}
private var tableView: TableView!
private let cellReuseId = "TableCell"
private let data: [UIColor] = [UIColor(white: 0.4, alpha: 1), UIColor(white: 0.5, alpha: 1), UIColor(white: 0.6, alpha: 1), UIColor(white: 0.7, alpha: 1)]
private var dataRepeatCount = 1
override func viewDidLoad() {
super.viewDidLoad()
func createTable(in: UIView) -> TableView {
let tableView = TableView(frame: CGRect.zero)
tableView.separatorStyle = .none
tableView.translatesAutoresizingMaskIntoConstraints = false
`in`.addSubview(tableView)
tableView.centerXAnchor.constraint(equalTo: `in`.centerXAnchor).isActive = true
tableView.centerYAnchor.constraint(equalTo: `in`.centerYAnchor).isActive = true
tableView.widthAnchor.constraint(equalTo: `in`.widthAnchor, multiplier: 1).isActive = true
tableView.heightAnchor.constraint(equalTo: `in`.heightAnchor, multiplier: 0.8).isActive = true
tableView.dataSource = self
tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellReuseId)
return tableView
}
func addHeader(to: UITableView) {
let header = UIView(frame: CGRect(x: 0, y: 0, width: 0, height: 50))
to.tableHeaderView = header
let color = UIColor.black
let offset: CGFloat = 64
let add = UIButton(type: .system)
add.setTitle("Add", for: .normal)
add.layer.borderColor = color.cgColor
add.layer.borderWidth = 1
add.layer.cornerRadius = 5
add.tintColor = color
add.contentEdgeInsets = UIEdgeInsets.init(top: 8, left: 8, bottom: 8, right: 8)
add.addTarget(self, action: #selector(addRows), for: .touchUpInside)
add.translatesAutoresizingMaskIntoConstraints = false
header.addSubview(add)
add.centerXAnchor.constraint(equalTo: to.centerXAnchor, constant: -offset).isActive = true
add.centerYAnchor.constraint(equalTo: header.centerYAnchor).isActive = true
let remove = UIButton(type: .system)
remove.setTitle("Remove", for: .normal)
remove.layer.borderColor = color.cgColor
remove.layer.borderWidth = 1
remove.layer.cornerRadius = 5
remove.tintColor = color
remove.contentEdgeInsets = UIEdgeInsets.init(top: 8, left: 8, bottom: 8, right: 8)
remove.addTarget(self, action: #selector(removeRows), for: .touchUpInside)
remove.translatesAutoresizingMaskIntoConstraints = false
header.addSubview(remove)
remove.centerXAnchor.constraint(equalTo: header.centerXAnchor, constant: offset).isActive = true
remove.centerYAnchor.constraint(equalTo: header.centerYAnchor).isActive = true
}
func addFooter(to: UITableView) {
let footer = FooterView(frame: CGRect(x: 0, y: 0, width: 0, height: 50))
footer.layer.borderWidth = 3
footer.layer.borderColor = UIColor.red.cgColor
//footer.contentMode = .redraw
to.tableFooterView = footer
}
tableView = createTable(in: view)
addHeader(to: tableView)
addFooter(to: tableView)
view.backgroundColor = .white
tableView.backgroundColor = .black // UIColor(white: 0.2, alpha: 1)
tableView.tableHeaderView!.backgroundColor = .cyan // UIColor(white: 0, alpha: 1)
tableView.tableFooterView!.backgroundColor = .white // UIColor(white: 0, alpha: 1)
}
#objc private func addRows() {
dataRepeatCount += 1
tableView.reloadData()
}
#objc private func removeRows() {
dataRepeatCount -= dataRepeatCount > 0 ? 1 : 0
tableView.reloadData()
}
}
extension TableViewController : UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
guard section == 0 else { fatalError("Unexpected section: \(section)") }
return dataRepeatCount * data.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellReuseId, for: indexPath)
cell.textLabel?.textAlignment = .center
cell.backgroundColor = data[indexPath.row % data.count]
cell.textLabel?.textColor = .white
cell.textLabel?.text = "\(indexPath.row)"
return cell
}
}

Table View Displays Below Screen

My issue is that the last cell in my TableView is below the screen view and to see it you must scroll up and hold your position. At a neutral position where you dont scroll up, you cant see the last cell. Everything seemed fine until i changed the size of the cells. Here is my code:
class ViewController: UIViewController,UITableViewDelegate,UITableViewDataSource {
//MARK : Properties
var tableView = UITableView()
var items: [String] = ["Age", "Gender", "Smoking Hx", "Occup. -Ag", "Family Hx", "Chronic Lung Disease Radiology", "Chronic Lung Disease Hx", "Nodule Border", "Nodule Location", "Satellite Lesions", "Nodule Pattern Cavity", "Nodule Size"]
var navigationBar = NavigationBar()
var gender = GenderView()
override func viewDidLoad() {
super.viewDidLoad()
//Create TableView
tableView.frame = CGRectMake(0, self.view.bounds.height * 0.097, self.view.bounds.width, self.view.bounds.height);
tableView.delegate = self
tableView.dataSource = self
tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "cell")
self.view.addSubview(tableView)
//Create Navigation Bar with custom class
self.navigationBar = NavigationBar(frame: CGRect(x: 0, y: 0, width: self.view.bounds.width, height: self.view.bounds.height * 0.097))
self.view.addSubview(navigationBar)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.items.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell:UITableViewCell = tableView.dequeueReusableCellWithIdentifier("cell")! as UITableViewCell
cell.textLabel?.text = self.items[indexPath.row]
//Cell wont turn grey when selected
cell.selectionStyle = UITableViewCellSelectionStyle.None
return cell
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return self.view.bounds.height * 0.095
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
print("You selected cell #\(indexPath.row)!")
}
}
The only thing i could think of causing this issue is that instead of me creating a navigation bar, i created a "navigationBar" using a custom UIView() class. I then start the table view at the bottom of the navigation bar. Any idea how to fix this?
Here is the NavigationBar.swift:
class NavigationBar: UIView {
var navigationBar = UIView()
var header = UILabel()
var lineBorder = UIView()
override init(frame: CGRect) {
super.init(frame: frame)
self.frame = frame
setUpView()
}
func setUpView(){
//Create Navigation Bar
navigationBar.frame = CGRectMake(0, 0, self.bounds.width, self.bounds.height)
navigationBar.backgroundColor = UIColor.whiteColor()
self.addSubview(navigationBar)
//Create Line Border
lineBorder.frame = CGRectMake(0, self.bounds.height, self.bounds.width, self.bounds.height * 0.005)
lineBorder.backgroundColor = UIColor.grayColor()
self.addSubview(lineBorder)
header.frame = CGRectMake(0, 0, 200, 200)
header.font = UIFont(name: "Helvetica", size: 17)
header.text = "Nodule Risk Calculator"
//header.backgroundColor = UIColor.blackColor()
self.addSubview(header)
header.translatesAutoresizingMaskIntoConstraints = false
header.centerHorizontallyTo(navigationBar, padding: 0)
header.centerVerticallyTo(navigationBar, padding: 9)
}
func hide(){
self.removeFromSuperview()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
tableView.frame = CGRectMake(0, self.view.bounds.height * 0.097, self.view.bounds.width, self.view.bounds.height);
has problem. The origin.y is not 0, and you still feed the whole height. So the table view will have some area below the screen
Try this:
tableView.frame = CGRectMake(0, self.view.bounds.height * 0.097, self.view.bounds.width,
self.view.bounds.height - self.view.bounds.height * 0.097);
self.navigationBar = NavigationBar(frame: CGRect(x: 0, y: 0, width: self.view.bounds.width, height: self.view.bounds.height * 0.097))
self.view.addSubview(navigationBar)
var yAxis : Float = self.navigationBar.frame.height
var tblHeight : Float = self.view.bounds.height - yAxis
tableView.frame = CGRectMake(0, yAxis, self.view.bounds.width, tblHeight)
self.view.addSubview(tableView)
tableView.delegate = self
tableView.dataSource = self
tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "cell")

table view with dynamic table rows and with NSLayoutConstraint’s programmatically

I implemented a table view with dynamic table rows and with NSLayoutConstraint’s programmatically. However, I encounter differences between the iPhone 5/5s/6s and iPhone 6s plus simulator.
What I basically did:
Creating a UIView (containerView) on a ScrollView (scrollView)
Creating a UITableView (infoTableView) on the containerView
Defining dynamic row heights for infoTableView
Registering a UITableViewCell (InfoTableViewCell) on infoTableView
Creating two UILabels (infoLabel and infoText) on a InfoTableViewCell
Defining horizontal and vertical constraints to infoText
When running this code below on an iPhone 5/5s/6s simulator the table row height is determined properly, and the labels are properly constrained to the table row. However, when I simulate the code on an iPhone 6 the data is not displayed correctly.
See Example.
How could this difference be explained? Are the constraints set correctly? Or am I missing some code?
A related question is about the total tableview height. I set this height in viewDidLayoutSubviews, but it is currently set to constant value since the TableHeight seems not yet been initialized here. How should the height be determined?
I hope that anybody could help.
My code:
import UIKit
class ViewController: UIViewController,UIScrollViewDelegate, UITableViewDataSource, UITableViewDelegate {
var scrollView : UIScrollView!
var containerView : UIView!
var infoTableView: UITableView!
var infoLabels = [String]()
var infoData = [String]()
override func viewDidLoad() {
super.viewDidLoad()
scrollView = UIScrollView()
scrollView.delegate = self
scrollView.contentSize = CGSizeMake(view.bounds.width, 1000)
containerView = UIView()
infoLabels = ["Label1", "Label2", "Label3"]
infoData = ["Label1Test1 Label1Test2 Label1Test3 Label1Test4 Label1Test5",
"Label3Test1 Label2Test2 Label2Test3 Label2Test4 Label2Test5",
"Label3Test1 Label3Test2 Label3Test3 Label3Test4 Label3Test5"]
// Create information TableView
infoTableView = UITableView()
infoTableView.registerClass(InfoTableViewCell.self, forCellReuseIdentifier: "Cell")
infoTableView.delegate = self
infoTableView.dataSource = self
infoTableView.estimatedRowHeight = 25
infoTableView.rowHeight = UITableViewAutomaticDimension
containerView.addSubview(infoTableView)
scrollView.addSubview(containerView)
view.addSubview(scrollView)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
scrollView.frame = view.bounds
containerView.frame = CGRectMake(0, 0, scrollView.contentSize.width, scrollView.contentSize.height)
// --------------------------------------------
// How to determine height of table view??????
let infoTableHeight: CGFloat = 200
//infoTableView.frame.size.height => Is nil during initialization!!!
infoTableView.frame = CGRectMake(5, 50, self.view.bounds.width - 10, infoTableHeight)
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return infoData.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let textCellIdentifier = "Cell"
let cell = tableView.dequeueReusableCellWithIdentifier(textCellIdentifier, forIndexPath: indexPath) as! InfoTableViewCell
let infoLabel = infoLabels[indexPath.row]
let infoDataItem = infoData[indexPath.row]
cell.infoLabel.text = infoLabel
cell.infoText.text = infoDataItem
cell.selectionStyle = .None
return cell
}
}
class InfoTableViewCell: UITableViewCell {
var infoLabel = UILabel()
var infoText = UILabel()
override init(style: UITableViewCellStyle, reuseIdentifier: String!) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
infoText.textAlignment = NSTextAlignment.Left
infoText.font = UIFont.systemFontOfSize(14)
infoText.baselineAdjustment = .AlignCenters
infoText.numberOfLines = 0
infoText.translatesAutoresizingMaskIntoConstraints = false
infoText.lineBreakMode = NSLineBreakMode.ByWordWrapping
self.contentView.addSubview(infoText)
let views = ["infoText" : infoText]
let hConstraint = NSLayoutConstraint.constraintsWithVisualFormat("H:|-[infoText]-|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: views)
self.contentView.addConstraints(hConstraint)
let vConstraint = NSLayoutConstraint.constraintsWithVisualFormat("V:|-[infoText]-|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: views)
self.contentView.addConstraints(vConstraint)
infoLabel.textAlignment = NSTextAlignment.Left
infoLabel.font = UIFont.boldSystemFontOfSize(14)
infoLabel.baselineAdjustment = .AlignCenters
infoLabel.numberOfLines = 0
self.contentView.addSubview(infoLabel)
}
required init(coder decoder: NSCoder) {
super.init(coder: decoder)!
}
override func awakeFromNib() {
super.awakeFromNib()
}
override func layoutSubviews() {
super.layoutSubviews()
let width = frame.width-120
let height = frame.size.height
infoLabel.frame = CGRectMake(10, 0, 100, height)
infoText.frame = CGRectMake(110, 0, width, height)
}
}

Custom Table View Cell Drawing Wrong Height When Scrolling

I'm customizing my table view cell inside the willDisplayCell method. For some reason it is drawing some subviews with the wrong height while scrolling (see video). I can't figure out why...
override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
cell.contentView.backgroundColor=UIColor.clearColor()
cell.reloadInputViews()
let height = cell.frame.height - 15
var whiteRoundedCornerView:UIView!
whiteRoundedCornerView=UIView(frame: CGRectMake(7,10,self.view.bounds.width-14,height))
whiteRoundedCornerView.backgroundColor=getColorByID(colorID!)
whiteRoundedCornerView.layer.masksToBounds=false
whiteRoundedCornerView.layer.shadowOpacity = 1.55;
whiteRoundedCornerView.layer.shadowOffset = CGSizeMake(1, 0);
whiteRoundedCornerView.layer.shadowColor=UIColor.grayColor().CGColor
whiteRoundedCornerView.layer.cornerRadius=5.0
whiteRoundedCornerView.layer.shadowOffset=CGSizeMake(-1, -1)
whiteRoundedCornerView.layer.shadowOpacity=0.5
whiteRoundedCornerView.layer.shouldRasterize = true
whiteRoundedCornerView.layer.rasterizationScale = UIScreen.mainScreen().scale
if cell.contentView.subviews.count < 6 { // to avoid multible subview adding when scrolling...
cell.contentView.addSubview(whiteRoundedCornerView)
cell.contentView.sendSubviewToBack(whiteRoundedCornerView)
}
}
Here is a video of the issue:
https://vid.me/QeV0
Update:
I also tried to move the code to layoutSubviews but still the same issue...
override func layoutSubviews() {
super.layoutSubviews()
let height = self.frame.height - 15
var whiteRoundedCornerView:UIView!
whiteRoundedCornerView=UIView(frame: CGRectMake(7,10,UIScreen.mainScreen().bounds.width-14,height))
whiteRoundedCornerView.backgroundColor=UIColor.greenColor()//getColorByID(colorID!)
whiteRoundedCornerView.layer.masksToBounds=false
whiteRoundedCornerView.layer.shadowOpacity = 1.55;
whiteRoundedCornerView.layer.shadowOffset = CGSizeMake(1, 0);
whiteRoundedCornerView.layer.shadowColor=UIColor.grayColor().CGColor
whiteRoundedCornerView.layer.cornerRadius=5.0
whiteRoundedCornerView.layer.shadowOffset=CGSizeMake(-1, -1)
whiteRoundedCornerView.layer.shadowOpacity=0.5
whiteRoundedCornerView.layer.shouldRasterize = true
whiteRoundedCornerView.layer.rasterizationScale = UIScreen.mainScreen().scale
if self.contentView.subviews.count < 6 {
self.contentView.addSubview(whiteRoundedCornerView)
self.contentView.sendSubviewToBack(whiteRoundedCornerView)
}
}
If i don't check the tableview.subviews.count it will add tons of subviews to the cell while scrolling.
I want to change the height of the existing subview when the cell is reused.
You have a check to not add the subviews multiple times. When that case happens you already have added the subviews earlier but their height may now be wrong and you must update them.
UITableView reuses cells so a new cell with different height might already contain your subviews but with the wrong height since you don't update it.
You should cleanly separate the set-up of your subview and the layout like so:
class MyCell: UITableViewCell {
private let whiteRoundedCornerView = UIView()
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setUp()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setUp()
}
override func layoutSubviews() {
super.layoutSubviews()
let bounds = self.bounds
var whiteRoundedCornerViewFrame = CGRect()
whiteRoundedCornerViewFrame.origin.x = 7
whiteRoundedCornerViewFrame.origin.y = 10
whiteRoundedCornerViewFrame.size.width = bounds.width - 7 - whiteRoundedCornerViewFrame.origin.x
whiteRoundedCornerViewFrame.size.height = bounds.height - 5 - whiteRoundedCornerViewFrame.origin.y
whiteRoundedCornerView.frame = whiteRoundedCornerViewFrame
}
private func setUp() {
setUpWhiteRoundedCornerView()
}
private func setUpWhiteRoundedCornerView() {
let child = whiteRoundedCornerView
child.backgroundColor = .greenColor()
let layer = child.layer
layer.cornerRadius = 5.0
layer.rasterizationScale = UIScreen.mainScreen().scale
layer.shadowColor = UIColor.grayColor().CGColor
layer.shadowOffset = CGSize(width: -1, height: -1)
layer.shadowOpacity = 0.5
layer.shouldRasterize = true
insertSubview(child, atIndex: 0)
}
}
If the data on different rows is of different size then you have to set the height of the tableview cell dynamically
Here is the code:
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat{
var height:CGFloat
let constraint1:CGSize = CGSizeMake(self.view.frame.size.width/2-70, 5000.0)
let string="eefdgvfgfhgfgfhgfgjhidffjo;dkfnkjfldjfkjfkjfldfjkkjhbkjflfjihkfjkfjgfkjfkgfkgjlfgnfjgnfgnfgbkgmfjgfkgjfkggjlfgklkgkgkglkgggfkglgkkgjlgkgk"
let answerSize = string.boundingRectWithSize(constraint1, options: NSStringDrawingOptions.UsesLineFragmentOrigin, attributes: [NSFontAttributeName: UIFont.systemFontOfSize(17.0)], context: nil)
let questionSize = string.boundingRectWithSize(constraint1, options: NSStringDrawingOptions.UsesLineFragmentOrigin, attributes: [NSFontAttributeName: UIFont.systemFontOfSize(17.0)], context: nil)
height=answerSize.height + questionSize.height
return height
}

Resources