Swift - Table View Scrolling past header understatus bar - uitableview

I'm trying to write a check in App, where after the user enters information on the first view the view changes to a tableView of meetings. Currently I'm facing two issues; the first issue is the cell scroll past the header and under the status bar, How do I fix this? the second issue is scrolling down pulls the header with it, is there a way to change this?
I looked online a bit, and some people suggested using a Nav Controller and putting a UItable view inside of it. I'm trying to avoid the StoryBoard so I'm wondering how do I do this using code.
Here is my code so far
class MeetingsViewController: UITableViewController, UITableViewDelegate, {
#IBOutlet var meetingsView : UITableView
var meetings = []
override func viewDidLoad() {
super.viewDidLoad()
title = "Meetings"
![enter image description here][1]tableView.registerClass(MeetingCell.self, forCellReuseIdentifier: "cell")
let edgeInsets = UIEdgeInsetsMake(20, 0, 0, 0)
self.tableView.contentInset = edgeInsets
self.tableView.scrollIndicatorInsets = edgeInsets
}
override func tableView(tableView: UITableView?, numberOfRowsInSection section: Int) -> Int {
return 10
}
override func tableView(tableView: UITableView!, titleForHeaderInSection section: Int) -> String!
{
return "Meetings"
}
override func tableView(tableView: UITableView?, cellForRowAtIndexPath indexPath: NSIndexPath?) -> UITableViewCell? {
let cell = tableView!.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as MeetingCell
if let path = indexPath {
//let entry = news[path.row] as NSDictionary
cell.text = "Meeting Name "
cell.detailTextLabel.text = "Time "
}
return cell
}
}

Try this in your controller's init():
let height = UIApplication.sharedApplication().statusBarFrame.size.height
let insets = UIEdgeInsets(top: height, left: 0, bottom: 0, right: 0)
self.tableView.contentInset = insets
self.tableView.scrollIndicatorInsets = insets

Related

Swift: TableView is showing some of the arrays data but not all

I have two tableviews inside my stack view. I am resizing them depending on the amount of data that is retrieved from Firestore. The issue I am facing is whilst the tableview is resize the top table view "ingredientsTV" shows all the data where as the "instructionsTV" only shows some of the data. My array.count displays the correct number of items in the array but them items are not getting displayed.
//Code for resize tableviews
override func viewWillLayoutSubviews() {
super.updateViewConstraints()
self.ingredientsTVHeight?.constant = self.ingredientsTV.contentSize.height
self.instructionsTVHeight.constant = self.instructionsTV.contentSize.height
self.ingredientsTV.contentInset = UIEdgeInsets(top: 0, left: -20, bottom: 0, right: 0)
self.instructionsTV.contentInset = UIEdgeInsets(top: 0, left: -20, bottom: 0, right: 0)
}
//setupview, called in viewdidload
//MARK: Functions
private func setupView() {
ingredientsTV.delegate = self
ingredientsTV.dataSource = self
instructionsTV.delegate = self
instructionsTV.dataSource = self
recipeImage.layer.cornerRadius = 5
recipeNameLbl.text = recipe.name
prepTimeLbl.text = recipe.prepTime
cookTimeLbl.text = recipe.cookTime
servesLabel.text = recipe.serves
if let url = URL(string: recipe.imageUrl) {
recipeImage.kf.setImage(with: url)
recipeImage.layer.cornerRadius = 5
}
}
//MARK: Tableview functions
extension PocketChefRecipeDetailsVC {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if (tableView == self.ingredientsTV) {
return recipe.ingredients.count
}else {
return recipe.method.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if (tableView == self.ingredientsTV) {
let cell = ingredientsTV.dequeueReusableCell(withIdentifier: "ingredientsCell", for: indexPath) as? ingredientsCell
cell?.ingredientsLbl.text = recipe.ingredients[indexPath.row]
return cell!
}else {
let cellB = instructionsTV.dequeueReusableCell(withIdentifier: "instructionsCell", for: indexPath) as? InstructionsCell
cellB?.instructionsLbl.text = recipe.method[indexPath.row]
return cellB!
}
}
}
*Recipe data is getting passed from previous view controller
I'm going to take a shot in the dark and say that maybe your stack view needs to be re laid out after you reload data.
Make sure to call
// after ingredientsTV.reloadData() and instructionsTV.reloadData() gets called
stackview.setNeedsLayout()
You can try adding a fixed height to each row and see if it has any populated data or not. Add this to your extension
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 100
}
Additionally, you can also set the background color of the cell in cellForRowAt() just to ensure the rows are visible.
Note: Do check if your stack view's constraints are set for all 4 sides.

UITableViewHeaderFooterView not showing customisations when first loaded

With Swift 3, I am using a subclass of UITableViewHeaderFooterView (called HeaderView) for the header sections on my TableView.
After dequeueing HeaderView, I customise it by
(1) setting the textLabel.textColor = UIColor.red, and (2) adding a subview to it.
When the application first loads, the table view loads up the headers but they have (what I assume is) the 'default' view (with textLabel.textColor being grey, and without my added subview). When I start scrolling and it starts dequeueing more HeaderViews, then the HeaderViews start coming up correctly, until there are eventually no more 'default' formatted HeaderViews.
Subsequent loads of the app no longer shows the 'default' view.
Alternatives considered
I know that this could be done by making my HeaderView a subclass of
UITableViewCell and customising it from the Storyboard, but that
seems like more of a workaround to use a prototype cell when there is
a UITableViewHeaderFooterView class that was designated for headers
Similarly it could be done with a XIB file, but even in Xcode 8 when
creating a subclass of UITableViewHeaderFooterView it doesn't allow
you to create an XIB file (so there must be some reason..)
Any comments/answers explaining why this is happening and how to resolve it are really appreciated!
UPDATE
As requested I've added in the code to show what I've done- you can recreate the problem with the code below and the usual setting up a TableViewController in the Storyboard (Swift 3, Xcode 8.2, Simulating on iOS 10.2 for iPhone 7)
ListTableViewController.swift
import UIKit
class ListTableViewController: UITableViewController {
// List of titles for each header
var titles: [String] {
var titles = [String]()
for i in 1...100 {
titles.append("List \(i)")
}
return titles as [String]
}
// Register view for header in here
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(ListHeaderView.self, forHeaderFooterViewReuseIdentifier: "Header")
}
// Table view data source
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let dequeuedCell = tableView.dequeueReusableHeaderFooterView(withIdentifier: "Header")
if let cell = dequeuedCell as? ListHeaderView {
cell.title = titles[section]
}
return dequeuedCell
}
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 44
}
override func numberOfSections(in tableView: UITableView) -> Int {
return titles.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 0
}
}
ListHeaderView.swift
import UIKit
class ListHeaderView: UITableViewHeaderFooterView {
var title: String? {
didSet {
updateUI()
}
}
private func updateUI() {
textLabel?.textColor = UIColor.red
textLabel?.text = title!
let separatorFrame = CGRect(x: 0, y: frame.height-1, width: frame.width, height: 0.25)
let separator = UIView(frame: separatorFrame)
separator.backgroundColor = UIColor.red
contentView.addSubview(separator)
}
}
Here is a screen shot of when the grey headers (screen is full of them upon initial load) and the customised red headers which start to appear upon scrolling.
For anyone interested, seems like this is a bug for which the best solution at this stage is to configure properties such as textColor on the header view in the tableView delegate method willDisplayHeaderView. Doing so 'last minute' just before the view appears allows you to override whatever configurations the system tries to force on the font etc.
Credit to answer found here
Troubles with changing the font size in UITableViewHeaderFooterView
Use this below code
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let dequeuedCell : ListHeaderView = tableView.dequeueReusableHeaderFooterView(withIdentifier: "Header") as? ListHeaderView
cell.title = titles[section]
cell.tittle.textcolor = uicolor.red
return dequeuedCell
}

Swift: Change TableViewCell Height Dynamically and change WebView Height Dynamically

I have a TableViewController, inside the TableViewCell, I have a UIWebView. I want the UIWebView to display some content from the internet, but I don't want the scroll effect, I want the WebView to have a dynamic height based on the length of the content. In addition, I want the TableViewCell to be able to adjust its cell height dynamically based on the dynamic height of WebView. Is this possible?
This is how I implemented my TableViewController:
class DetailTableViewController: UITableViewController {
var passPost: Posts = Posts()
var author: Author = Author()
override func viewDidLoad() {
super.viewDidLoad()
getAuthor()
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("detailCell")
let postImageUrlString = passPost.postThumbnailUrlString
let postImageUrl = NSURL(string: postImageUrlString)
let size = CGSize(width: 414.0, height:212.0 )
let filter = AspectScaledToFillSizeFilter(size: size)
(cell?.contentView.viewWithTag(1) as! UIImageView).af_setImageWithURL(postImageUrl!, filter: filter)
//Set Author Avatar
let authorAvatarUrlString = author.authorAvatarUrlString
let authorAvatarUrl = NSURL(string: authorAvatarUrlString)
//Mark - Give Author Avatar a Round Corner
let filter2 = AspectScaledToFillSizeWithRoundedCornersFilter(size: (cell?.contentView.viewWithTag(2) as! UIImageView).frame.size, radius: 20.0)
(cell?.contentView.viewWithTag(2) as! UIImageView).af_setImageWithURL(authorAvatarUrl!, filter: filter2)
//Set Post Title and Content and so on
(cell?.contentView.viewWithTag(4) as! UILabel).text = passPost.postTitle
(cell?.contentView.viewWithTag(6) as! UIWebView).loadHTMLString("\(passPost.postContent)", baseURL: nil)
for the heightForCellAtIndexPath I did
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
let cell = tableView.dequeueReusableCellWithIdentifier("cell") as! tableViewCell
tableView.rowHeight = cell.theWebView.scrollView.contentSize.height
return tableView.rowHeight
}
This is working fine, except the WebView has a scroll effect, and the height is limited due to the limitation of the TableViewCell. So, how to achieve what I need?
You need to set the height of your cell in the UITableViewDelegate method
tableView(_:heightForRowAt:)
You would do all your calculations on each individual cell height in this method and return it. If you want to display the UIWebView in it's whole without the need to scroll, you should return the height of the UIWebView's scroll view contentView – plus any height for anything else you might want to display in this cell.
Follow the steps:
1) set leading, trailing, top, bottom constraint 0(zero) from webview to tableviewCell
2) Now no need to call HeightForRow method of tableView.
3) in webview delegate method
func webViewDidFinishLoad(webView: UIWebView)
{
var frame = cell.webView.frame
frame.size.height = 1
let fittingSize = cell.webView.sizeThatFits(CGSizeZero)
frame.size = fittingSize
}
4) webview scrollenabled = false
You can return, in heightForRowAtIndexPath:
tableView.rowHeight = UITableViewAutomaticDimension
You also need to have a value setted in estimatedRowHeight, like
tableView.estimatedRowHeight = 85.0
If you have all the constraints defined correctly in the Storyboard (Or programmatically), you shouldn't get any error.
Reference: https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG/WorkingwithSelf-SizingTableViewCells.html

Swift: UITableView Cell Disappears When Using UIView.animateWithDuration

I am programmatically adding a UITableView as a subview of a view that uses UIView.animateWithDuration to expand the view when a button is clicked from a single point to a full window. Basically, a box that starts as a point and expands to full size with an animation. I am having difficulties getting the table to populate with cells. At first, a cell was being created, but would disappear after quickly after the animation completed, after playing around with it, I have gotten the cell to remain after the animation is complete, but now the cell disappears when I tap on it. I don't understand what is going on here. Can someone please help?
Here is my code. Note, I have removed what I believe to be irrelevant to this problem to make the code easier to read.
class PokerLogSelectionView: UIViewController {
let logSelectionTableViewController = LogSelectionTableViewController()
let logSelectionTableView = UITableView()
// Irrelevant class variables removed
init(btn : PokerLogSelectionButton){
// Irrelevant view initialization code removed
// Display the subviews
self.displayLogListScrollView()
}
func displayLogListScrollView() {
// Frame is set to (0,0,0,0)
let frame = CGRect(x: self.subviewClosed, y: self.subviewClosed, width: self.subviewClosed, height: self.subviewClosed)
logSelectionTableView.delegate = self.logSelectionTableViewController
logSelectionTableView.dataSource = self.logSelectionTableViewController
// Set the frame of the table view
logSelectionTableView.frame = frame
// Give it rounded edges
logSelectionTableView.layer.cornerRadius = 10
// Remove the cell divider lines
logSelectionTableView.separatorStyle = UITableViewCellSeparatorStyle.None
logSelectionTableView.backgroundColor = logSelectionViewContentScrollViewColor
self.view.addSubview(logSelectionTableView)
//self.logSelectionTableView.reloadData()
//self.addChildViewController(logSelectionTableViewController)
}
override func viewDidAppear(animated: Bool) {
// Create animation
let timeInterval : NSTimeInterval = 0.5
let delay : NSTimeInterval = 0
UIView.animateWithDuration(timeInterval, delay: delay, options: UIViewAnimationOptions.CurveEaseOut, animations: {
// Irrelevant code removed
// Set the size and position of the view and subviews after the animation is complete
self.view.frame = CGRect(x: self.frameXopen, y: self.frameYopen, width: self.frameWopen, height: self.frameHopen)
self.logSelectionTableView.frame = CGRect(x: self.subviewXopen, y: self.svYopen, width: self.subviewWopen, height: self.svHopen)
}, completion: { finished in
self.addChildViewController(self.logSelectionTableViewController)
})
}
}
class LogSelectionTableViewController : UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.registerClass(LogSelectionCell.self, forCellReuseIdentifier: "logCell")
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return pokerLibrary.logNames.count
}
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 20
}
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
return true
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
print("Selected row: \(indexPath.row)")
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if let cell : LogSelectionCell = self.tableView.dequeueReusableCellWithIdentifier("logCell") as? LogSelectionCell {
cell.selectionStyle = UITableViewCellSelectionStyle.None
cell.textLabel!.text = pokerLibrary.logNames[indexPath.row].name
return cell
}
fatalError("Could not dequeue cell of type 'LogSelectionCell'")
}
}
Note: I can see the tableview after the animation is complete. The color is different than the view in the background view and the tableview does not disappear, just the cell. I expect there to be 1 cell, and I have printed out the number of rows in section 0 and it always returns 1.
Thanks for the help!
Edit:
Here is a screenshot of the view hierarchy before the cell disappears.
Here is a screenshot of the view hierarchy after I tap the cell and it disappears.
I overrode the touchesBegan method in my custom cell and did not call its superclass method. This stopped the cell from disappearing when I tap it, but it still disappears when I scroll the tableView.

Frequent update table view failed on swift

I have a tableview need to be updated very second. The code are as following. I design the headerview to have a dropdown function, when the header tap the rest are displayed. The code will crashes when I am trying to tap the header, the thread stops, xcode is not giving any hint on how and why.
func didListOfBLEDevicesUpdate(newDevice: BLEDevice)
{
println("receivedDevice from scanner every second: \(newDevice.deviceName)")
self.deviceTableView.reloadData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return BLEDevice.listOfDevices.items.count
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1;
}
func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return BLEDevice.listOfDevices.items[section].deviceName
}
func tableView(tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 1
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if(IsExpandedMode[indexPath.section] == true){
return 400
}
return 70;
}
func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerView = UIView(frame: CGRectMake(0, 0, tableView.frame.size.width, 40))
headerView.backgroundColor = UIColor.grayColor()
headerView.tag = section
let headerString = UILabel(frame: CGRect(x: 10, y: 10, width: tableView.frame.size.width-10, height: 30)) as UILabel
headerString.text = BLEDevice.listOfDevices.items[section].deviceName
headerView .addSubview(headerString)
let headerTapped = UITapGestureRecognizer (target: self, action:"sectionHeaderTapped:")
headerView .addGestureRecognizer(headerTapped)
return headerView
}
func sectionHeaderTapped(recognizer: UITapGestureRecognizer) {
println("Tapping working")
println(recognizer.view?.tag)
var indexPath : NSIndexPath = NSIndexPath(forRow: 0, inSection:(recognizer.view?.tag as Int!)!)
if (indexPath.row == 0) {
var collapsed = self.IsExpandedMode [indexPath.section]
collapsed = !collapsed;
self.IsExpandedMode[indexPath.section] = collapsed
//reload specific section animated
var range = NSMakeRange(indexPath.section, 1)
var sectionToReload = NSIndexSet(indexesInRange: range)
self.deviceTableView.reloadSections(sectionToReload, withRowAnimation:UITableViewRowAnimation.Fade)
}
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell : DeviceTableViewCell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! DeviceTableViewCell
let row = indexPath.row
cell.deviceName!.text = BLEDevice.listOfDevices.items[row].deviceName
cell.connectionStatus.text = BLEDevice.listOfDevices.items[row].connectionStatus
cell.deviceSignalStrengthen.text = BLEDevice.listOfDevices.items[row].RSSI
cell.manufacturerData.text = BLEDevice.listOfDevices.items[row].advertisementPackage.cBAdvertisementDataManufacturerData
cell.serviceUUID.text = BLEDevice.listOfDevices.items[row].advertisementPackage.cBAdvertisementDataServiceUUIDs
cell.serviceData.text = DataConvertHelper.getNSDictionary(BLEDevice.listOfDevices.items[row].advertisementPackage.cBAdvertisementDataServiceData)
cell.TxPowerLevel.text = BLEDevice.listOfDevices.items[row].advertisementPackage.cBAdvertisementDataTxPowerLevel
cell.IsConnectable.text = DataConvertHelper.getBool(BLEDevice.listOfDevices.items[row].advertisementPackage.cBAdvertisementDataIsConnectable)
cell.solicitedServiceUUID.text = BLEDevice.listOfDevices.items[row].advertisementPackage.cBAdvertisementDataSolicitedServiceUUIDs
cell.shortenedLocalName.text = BLEDevice.listOfDevices.items[row].advertisementPackage.cBAdvertisementDataLocalName
return cell
}
Use reload sections and reload rows rather than reloading data
The method you have used to handle the table seems to be rather complex. An alternative would be as follows:
1) Assumption from you code is that each device is associated with a section. As noted in the comments, your cellForRorAtIndexPath method seems to be using [row] to index your data model, but the model is based on [section] as you always return the number of rows as 1 for every section and the number of sections is the number of devices.
2) Rather than using a header view for each section and having to add gesture recognizers, simply create a custom cell to represent the device and make this row 0 of the section.
3) So each device is associated with a section, and row 0 of each section is the header information cell, NOT a header view. Make the header view nil. You can use a header height to leave a gap between sections.
4) Add code to detect selection of cells. When the cell row is 0, its the header cell. If the device is collapsed, set it to be expanded and vice versa and reload the section.
5) Make a new custom cell for you dropdown information. this will be row 1 of any section which is showing information.
6) Update your number of rows in section to return 2 if expanded, or 1 if collapsed.
7) Update cellForRowAtIndexPath to return the header cell for row 0 and the detail cell for row 1. Make sure to fix the [row] indexing to be [section] indexing.
This gives you a table of device header cells, which when clicked insert a detail cell below and when clicked again remove it and no gesture recognizers needed.
You need to make sure that your data model updates are working correctly. Seems from your errors that you are not updating the data model properly: in particular removal of devices.

Resources