I have a UITableView with automatic sizing cells. The contents of each cell revolve around the counting of time, so the size of the cell could change every second. I am currently using a timer scheduled every 1 second to tell the table view to update the cell sizes:
Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(timeUpdateNotification), userInfo: nil, repeats: true)
#objc func timeUpdateNotification() {
// ... cells get notified to update contents here ...
tableView.beginUpdates()
tableView.endUpdates()
}
This works fairly well but has issues when the user taps to scroll to the top of the list. The animation is a bit janky and often times doesn't make it to the top. Is there a better way to handle this?
If you set a flag when the table is scrolling you can detect it in your timer function and not update while it is set. UITableView is a descendant of UIScrollView, so you can use some of the scroll view delegates to do this. If you override scrollViewShouldScrollToTop() and scrollViewDidScrollToTop() you will know when the scroll view is scrolling to the top and when it has finished.
override func scrollViewShouldScrollToTop(_ scrollView: UIScrollView) -> Bool {
NSLog("scrollViewShouldScrollToTop")
isScrolling = true
return true
}
override func scrollViewDidScrollToTop(_ scrollView: UIScrollView) {
NSLog("scrollViewDidScrollToTop")
isScrolling = false
}
You could also extend this to detect when the user is dragging/scrolling the view, to prevent the timer function from updating at these occasions too.
override func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
NSLog("scrollViewWillBeginDragging")
isScrolling = true
}
override func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
if (!decelerate) {
// Only catch if scrolling stopped
NSLog("scrollViewDidEndDragging")
isScrolling = false
}
}
override func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
NSLog("scrollViewDidEndDecelerating")
isScrolling = false
}
I have added some logging to the functions, so that you can see what is going on. You can remove these, of course.
Invalidate the timer when they click to scroll to top, then as soon as it reaches top start it again.
Edit: That way, it won't update content which may move content higher without updating the "top" of the table view.
I had a similar problem.
I solved it with the help of a manual height calculation for each cell.
var cellHeights: [Int: CGFloat] = [:]
In the cellForRowAtIndexPath method, calculate the height:
cellHeights[byRow] = cell.getCellHeight()
In the cell itself
func getCellHeight() -> CGFloat {
let userInfoHeight: CGFloat = userInfoHeightConstraint.constant
let actionHeight: CGFloat = actionViewHeightConstraint.constant
let descriptionBottom: CGFloat = descriptionBottomConstraint.constant
let descriptionWidth = self.frame.width - leftAvatarConstraint.constant - descriptionRightConstraint.constant
let descriptionHeight: CGFloat = descriptionLabel.textHeight(width: descriptionWidth)
let height = userInfoHeight + actionHeight + descriptionBottom + descriptionHeight
return height
}
extension UILabel {
func textHeight(width: CGFloat) -> CGFloat {
var textHeight: CGFloat = 0
if let text = self.text {
let customLabel = UILabel(frame: CGRect(x: 0, y: 0, width: width, height: .greatestFiniteMagnitude))
customLabel.numberOfLines = self.numberOfLines
customLabel.text = text
customLabel.font = self.font
customLabel.sizeToFit()
textHeight = customLabel.frame.height
}
return textHeight
}}
I'm using UITableViewAutomaticDimension.
I additionally have the functionality to change the size of the cell at various clicked on it.
Therefore, in my task, this works fine:
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return presenter.cellHeights[indexPath.row] ?? 0
}
But I think that you can immediately set the height:
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return presenter.cellHeights[indexPath.row] ?? 0
}
Related
I have a UITableView that is performing an animation on scroll, basically a sticky header, but instead it adjusts the height constraint of a UIView. I am getting really high CPU usage on scroll. Is there a better way to perform this animation?
Edit: I am not doing anything in the cellForRow at, function that would be causing this. It is definitely the scrollViewDidScroll function.
DidScroll:
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let offset = scrollView.contentOffset.y + tableViewContentInsets
if (lastPoint != nil) {
if (lastPoint! < offset) {
if (portfolioSummaryInitialHeight - offset < portfolioSummaryInitialHeight){
if (portfolioSummaryInitialHeight - offset > 0 ){
portfolioSummaryHeightConstraint.constant = portfolioSummaryInitialHeight - offset
} else {
portfolioSummaryHeightConstraint.constant = 0
}
}
} else {
if (portfolioSummaryInitialHeight - offset < portfolioSummaryInitialHeight){
if (portfolioSummaryInitialHeight - offset > 0 ){
portfolioSummaryHeightConstraint.constant = portfolioSummaryInitialHeight - offset
}
} else {
portfolioSummaryHeightConstraint.constant = portfolioSummaryInitialHeight
}
}
}
lastPoint = offset
}
Additional Variables:
var tableViewContentInsets: CGFloat = 80
var portfolioSummaryInitialHeight: CGFloat = 0 // equals portfolioSummaryInitialHeight at viewDidLoad
var lastPoint: CGFloat?
first of all, I'd advise you to check if you perform any other heavy operations on the main thread e.g data fetch or something similar. I've created a simple test project with a plain table view and a header. Here is the code which does the resizes header. According to the CPU report, it utilizes 8% of CPU in high peaks.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var headerHeightConstr: NSLayoutConstraint! //header's height constraint
#IBOutlet weak var tableView: UITableView!
private var headerHeight: CGFloat = 128.0 ////header's height constraint initial value
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.delegate = self
}
}
extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 100
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "reuseID") else {
return UITableViewCell(style: .default, reuseIdentifier: "reuseID")
}
return cell
}
}
extension ViewController: UITableViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if scrollView.contentOffset.y < 0 {
self.headerHeightConstr.constant += abs(scrollView.contentOffset.y)
} else if scrollView.contentOffset.y > 0 && self.headerHeightConstr.constant >= headerHeight {
self.headerHeightConstr.constant -= scrollView.contentOffset.y
if self.headerHeightConstr.constant < headerHeight {
self.headerHeightConstr.constant = headerHeight
}
}
}
}
This is kinda naive approach but it works, will try to answer your questions if needed.
Check your WillLayoutSubViews and DidLayoutSubViews functions. If you are doing lots of work in those functions then you need to add something like this:
var isScrolling: Bool = false
override func viewDidLayoutSubviews() {
if (isScrolling) {
return
} else {
super.viewDidLayoutSubviews()
}
}
While your tableView is scrolling set isScrolling = true in scrollViewDidEndDragging set isScrolling = false this will ensure that your class will not do excessive work while the tableView is scrolling but insure that it will do the work in other instances. Might not be the best way to do this but it certainly works.
I have a collection view where the cell is of the size exactly to the collectionView, so each cell should occupy the whole screen. I have implemented a functionality where the cell is snapped to the complete view whenever it's dragged or decelerated through the scroll. This is how the UX works.
https://drive.google.com/open?id=1v8-WxCQUzfu8V_k9zM1UCWsf_-Zz4dpr
What I want:
As you can see from the clip, the cell snaps to the whole screen. Now, I want to execute a method after it snaps. Not before or not when it's partially displayed.
Following is the code I have written for snapping effect :
func scrollToMostVisibleCell(){
let visibleRect = CGRect(origin: collectionView.contentOffset, size: collectionView.bounds.size)
let visiblePoint = CGPoint(x: visibleRect.midX, y: visibleRect.midY)
let visibleIndexPath = collectionView.indexPathForItem(at: visiblePoint)!
collectionView.scrollToItem(at: visibleIndexPath as IndexPath, at: .top, animated: true)
print("cell is ---> ", visibleIndexPath.row)
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
scrollToMostVisibleCell()
}
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
scrollToMostVisibleCell()
if !decelerate {
scrollToMostVisibleCell()
}
}
If I use willDisplayCell method, then it' just going to return me as soon as the cell is in the view, even if it's just peeping in the collectionView.
Is there a way where I can check if the cell is completely in the view and then I can perform a function?
I have scrapped the internet over this question, but ain't able to find a satisfactory answer.
Here is a complete example of a "full screen" vertical scrolling collection view controller, with paging enabled (5 solid color cells). When the cell has "snapped into place" it will trigger scrollViewDidEndDecelerating where you can get the index of the current cell and perform whatever actions you like.
Add a new UICollectionViewController to your storyboard, and assign its class to VerticalPagingCollectionViewController. No need to change any of the default settings for the controller in storyboard - it's all handled in the code below:
//
// VerticalPagingCollectionViewController.swift
//
// Created by Don Mag on 10/31/18.
//
import UIKit
private let reuseIdentifier = "Cell"
class VerticalPagingCollectionViewController: UICollectionViewController {
private var collectionViewFlowLayout: UICollectionViewFlowLayout {
return collectionViewLayout as! UICollectionViewFlowLayout
}
private var colors: [UIColor] = [.red, .green, .blue, .yellow, .orange]
override func viewDidLoad() {
super.viewDidLoad()
// Register cell classes
self.collectionView?.register(UICollectionViewCell.self, forCellWithReuseIdentifier: reuseIdentifier)
// enable paging
self.collectionView?.isPagingEnabled = true
// set section insets and item spacing to Zero
collectionViewFlowLayout.sectionInset = UIEdgeInsets.zero
collectionViewFlowLayout.minimumLineSpacing = 0
collectionViewFlowLayout.minimumInteritemSpacing = 0
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if let cv = collectionViewLayout.collectionView {
collectionViewFlowLayout.itemSize = cv.frame.size
}
}
override func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
if let iPath = collectionView?.indexPathsForVisibleItems.first {
print("DidEndDecelerating - visible cell is: ", iPath)
// do what you want here...
}
}
override func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return colors.count
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath)
cell.backgroundColor = colors[indexPath.item]
return cell
}
}
Using followedCollectionView.indexPathsForVisibleItems() to get visible cells visibleIndexPaths and check your indexPath is contained in visibleIndexPaths or not, before doing anything with cells.
Ref : #anhtu Check whether cell at indexPath is visible on screen UICollectionView
Also from Apple : var visibleCells: [UICollectionViewCell] { get } . Returns an array of visible cells currently displayed by the collection view.
I'm currently using a UICollectionView to display 3 images (each image spans the entire cell). I also have a UIPageControl that I placed on top of the UICollectionView. What I want to happen is to have the UIPageControl show the number of images (which in this case is 3), and also which image the user is currently viewing. The effect I am trying to go for is that of the Instagram app.
The way that I am currently using to achieve this effect is by placing the updating of the UIPageControl within the UICollectionView's willDisplay function, like so:
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
pictureDots.currentPage = indexPath.item
}
This manages to correctly hook up the paging effect between the collection view and page control. However, the problem that I have is that the UIPageControl starts off saying the user is on the third image, even though it is displaying the first image.
Does anyone know why this is happening and how to fix this problem?
Firstly add your UIPageControl into your storyboard with your UICollectionView, then connect them as outlets to your view controller.
#IBOutlet var pageControl: UIPageControl!
#IBOutlet var collectionView: UICollectionView!
Adjust your numberOfItemsInSection method in UICollectionViewDataSource to set the count of the page control to always be equal to the number of cells in the collection view.
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
let count = ...
pageControl.numberOfPages = count
pageControl.isHidden = !(count > 1)
return count
}
Lastly, using the UIScrollViewDelegate, we can tell which cell the UICollectionView stops on. If you are not using a UICollectionViewController, you may have to add the delegate protocol.
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
pageControl?.currentPage = Int(scrollView.contentOffset.x) / Int(scrollView.frame.width)
}
func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
pageControl?.currentPage = Int(scrollView.contentOffset.x) / Int(scrollView.frame.width)
}
This is possible because a UICollectionView is in fact a UIScrollView under the hood.
Step 1 Create the variables for Collection View and Page Control
#IBOutlet weak var collectionView: UICollectionView!
#IBOutlet var pageControl:UIPageControl!
Step 2 Set the number of pages of Page Control
override func viewDidLoad() {
super.viewDidLoad()
self.pageControl.numberOfPages = procedures.count
//Set the delegate
self.collectionView.delegate = self
}
*Step 3 In the scrollViewDidScroll function calculate the width of collection cell and the index for the current page.
extension YourCollectionVC: UICollectionViewDataSource, UICollectionViewDelegate {
override func scrollViewDidScroll(_ scrollView: UIScrollView) {
let witdh = scrollView.frame.width - (scrollView.contentInset.left*2)
let index = scrollView.contentOffset.x / witdh
let roundedIndex = round(index)
self.pageControl?.currentPage = Int(roundedIndex)
}
}
Note: This is for collection view displayed horizontally, for vertical direccion change the method.
Tested in swift 4.
Use this for smooth functioning.
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let witdh = scrollView.frame.width - (scrollView.contentInset.left*2)
let index = scrollView.contentOffset.x / witdh
let roundedIndex = round(index)
self.pageControl.currentPage = Int(roundedIndex)
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
pageControl.currentPage = Int(scrollView.contentOffset.x) / Int(scrollView.frame.width)
}
func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
pageControl.currentPage = Int(scrollView.contentOffset.x) / Int(scrollView.frame.width)
}
With help of Combine, we can easily connect UIPageControl with any ScrollView.
var collectionView: UICollectionView
var bin: Set<AnyCancellable> = []
var pageControl: UIPageControl
// setup observer
collectionView
.publisher(for: \.contentOffset)
.map { [unowned self] offset in
Int(round(offset.x / max(1, self.collectionView.bounds.width)))
}
.removeDuplicates()
.assign(to: \.currentPage, on: pageControl) // may cause memory leak use sink
.store(in: &bin)
//For every iPhone screen, it is working(Swift 5)...
func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
let offSet = scrollView.contentOffset.x
let width = scrollView.frame.width
let horizontalCenter = width / 2
_pageControl.currentPage = Int(offSet + horizontalCenter) / Int(width)
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
pageControl.currentPage = Int(scrollView.contentOffset.x) / Int(scrollView.frame.width)
}
I wonder if tableview has any built-in function to add infinite scroll/pagination.
Right now my VC looks like this:
var data: JSON! = []
override func viewDidLoad() {
super.viewDidLoad()
//Init start height of cell
self.tableView.estimatedRowHeight = 122
self.tableView.rowHeight = UITableViewAutomaticDimension
self.tableView.delegate = self
self.tableView.dataSource = self
savedLoader.startAnimation()
//Load first page
loadSaved(1)
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("aCell") as! SavedTableViewCell
let info = data[indexPath.row]
cell.configureWithData(info)
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
performSegueWithIdentifier("WebSegue", sender: indexPath)
tableView.deselectRowAtIndexPath(indexPath, animated: false)
}
I fetch my data using loadSaved(1) by giving the function the current page I want to load. The function makes a API request using alomofire then populate the var data: JSON! = [] with the data that should be displayed
So what I want to do is when I scroll to the bottom of the tableview loadSaved(2) should be called loading more data into the tableview
Looks like amar’s answer might be a better solution now, but here’s my answer from 2017 anyway:
The UITableViewDelegate has a tableView(_:willDisplay:forRowAt:) instance method which "tells the delegate the table view is about to draw a cell for a particular row."
In your case I would use it something like this:
override open func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
if indexPath.row == data.count-1 { //you might decide to load sooner than -1 I guess...
//load more into data here
}
}
Depending on your code, you may need some checks around this to ensure you don't end up in an infinite loop if you've loaded all your data...
No, the UITableView has not any built-in function to achieve the infinite scroll or load on-demand cells like you want. What you can use is the function scrollViewDidScroll(_:) in the UIScrollViewDelegate implemented by default in the UITableView and in this way know when the user scroll more than the original height defined in the UITableView.
For example like in this code:
var indexOfPageToRequest = 1
override func scrollViewDidScroll(scrollView: UIScrollView) {
// calculates where the user is in the y-axis
let offsetY = scrollView.contentOffset.y
let contentHeight = scrollView.contentSize.height
if offsetY > contentHeight - scrollView.frame.size.height {
// increments the number of the page to request
indexOfPageToRequest += 1
// call your API for more data
loadSaved(indexOfPageToRequest)
// tell the table view to reload with the new data
self.tableView.reloadData()
}
}
To achieve the result of add the rest of the elements at the end of the UITableView you should add the new elements to the data source, in your case data inside your function loadSaved(numberOfPage).
I hope this help you.
All the above answers are correct but for iOS 10 and above we have a very nice
func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath])
This is a prefetch delegate which needs to be set
tableView.prefetchDataSource = self
RayWeinderlich has a nice tutorial on the topic. Since Rays is a dependable site i am not posting code here
I have modified Victor's answer and used it as ,
var indexOfPageRequest = 1
var loadingStatus = false
func loadData(){
if !loadingStatus{
loadingStatus = true
viewModel.getData(pageIndex: indexOfPageRequest)
}
}
override func scrollViewDidScroll(_ scrollView: UIScrollView) {
// calculates where the user is in the y-axis
let offsetY = scrollView.contentOffset.y
let contentHeight = scrollView.contentSize.height
if offsetY > contentHeight - scrollView.frame.size.height {
// increments the number of the page to request
indexOfPageRequest += 1
// call your API for more data
loadData()
// tell the table view to reload with the new data
self.tableView.reloadData()
}
}
Reset loadingStatus to true when you receive data. Without checking if the view was already loading more data, the tableview was flickering.
Ravi's answer looks good. But as he pointed out in the end, the tableView flickers a lot if you use scrollViewDidScroll(_ scrollView: UIScrollView)
This is because you are trying to reload tableView every time you are scrolling the tableView.
Instead you could use scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) delegate method to determine whether you have scrolled enough and have reached almost the end of the tableView.
override func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
let offsetY = scrollView.contentOffset.y
let contentHeight = scrollView.contentSize.height
if offsetY > contentHeight - scrollView.frame.size.height {
indexOfPageRequest += 1
loadData()
self.tableView.reloadData()
}
}
Above Ans are also right, but may be some one help out this code also.
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath){
if(indexPath.row == self.arryOfData.count-1){
if(self.pageNumber <= self.resPgNumber){
if(remaining != 0){
let spinner = UIActivityIndicatorView(activityIndicatorStyle: .gray)
spinner.frame = CGRect(x: CGFloat(0), y: CGFloat(0), width: tableView.bounds.width, height: CGFloat(44))
spinner.startAnimating()
tableView.tableFooterView = spinner
tableView.tableFooterView?.isHidden = false
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
self.flgActivity = false
self.getActiveOrdersList()
}
}
else{
tableView.tableFooterView?.removeFromSuperview()
let view = UIView()
view.frame = CGRect(x: CGFloat(0), y: CGFloat(0), width: tableView.bounds.width, height: CGFloat(5))
tableView.tableFooterView = view
tableView.tableFooterView?.isHidden = true
}
}
else{
tableView.tableFooterView?.removeFromSuperview()
let view = UIView()
view.frame = CGRect(x: CGFloat(0), y: CGFloat(0), width: tableView.bounds.width, height: CGFloat(5))
tableView.tableFooterView = view
tableView.tableFooterView?.isHidden = true
}
}
else{
tableView.tableFooterView?.removeFromSuperview()
let view = UIView()
view.frame = CGRect(x: CGFloat(0), y: CGFloat(0), width: tableView.bounds.width, height: CGFloat(5))
tableView.tableFooterView = view
tableView.tableFooterView?.isHidden = true
}
}
There are number of ways we can do this. The essense of all different ways, is to load next set of data when user scroll to last. I have implemented it via adding an extra special cell at the end of tableView and when that cell gets loaded in willDisplay cell: forRowAtIndexPath: which triggers next set of fetching of data.
Athough this is simple to implement but in larger apps at times we need to implement it many places. To avoid this, I wrote a small framework which is non-intrusive and can be easyly integrated.
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.