SwiftUI view sizing is broken when added to UITableViewController - swift

I need to display a SwiftUI view in an existing UITableViewController, as the tableHeaderView. However, it seems that the sizing of the SwiftUI view is broken when it is added to a UITableViewController.
If I simply convert my SwiftUI View to a UIView using a UIHostingController and set it to the tableHeaderView, the view is displayed off screen:
func addHeaderView() {
let view = VerticalTextStack()
let hostingController = UIHostingController(rootView: view)
tableView.tableHeaderView = hostingController.view
}
Incorrect layout
To counteract this, I've tried to fix the height of the view several different ways. Adding an NSLayoutConstraint didn't do anything. When setting tableHeaderView.frame.size manually, the results were better, since at least now the view is displayed on-screen, but the multiline Texts become single-line and truncated.
As you can see here, the 2nd Text gets truncated:
Here's a simplified example showcasing the problem:
/// `UITableViewController` displaying a `UIView` as its `tableHeaderView`
class TableViewController: UITableViewController {
let cellReuseIdentifier = "cell"
let data = ["A", "B", "C", "D", "E"]
let themeManager = AppThemeManager()
// MARK: - UIViewController lifecycle
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellReuseIdentifier)
addHeaderView()
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
fixTableHeaderViewSize()
}
// MARK: - UITableViewDelegate
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
data.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier, for: indexPath)
cell.textLabel?.text = data[indexPath.row]
return cell
}
// MARK: - SwiftUI view
func fixTableHeaderViewSize() {
guard let tableHeaderView = tableView?.tableHeaderView else { return }
let expectedHeight = tableHeaderView.systemLayoutSizeFitting(UIView.layoutFittingExpandedSize).height
let expectedSize = CGSize(width: tableHeaderView.frame.width, height: expectedHeight)
tableHeaderView.frame.size = expectedSize
}
func addHeaderView() {
let view = VerticalTextStack()
let hostingController = UIHostingController(rootView: view)
tableView.tableHeaderView = hostingController.view
}
}
private struct VerticalTextStack: View {
let data = ["First", "I am a very long text that only fits in multiple lines. I still continue.", "Third"]
let themeManager = AppThemeManager()
var body: some View {
VStack(spacing: 10) {
ForEach(data, id: \.self) { value in
Text(value)
}
}
}
}
I've also tried moving addHeaderView to other UIViewController functions, such as viewWillLayoutSubviews, but that didn't change anything.
Setting lineLimit to nil or any large number on the Text inside VerticalTextStack and adding .layoutPriority(.greatestFiniteMagnitude) to the Text did not make the Text multiline either.

Here is a possible solution.
Change your add header view funcation with this.
func addHeaderView() {
let view = VerticalTextStack()
let hostingController = UIHostingController(rootView: view)
let headerViewMain = UIView()
headerViewMain.backgroundColor = .red
headerViewMain.addSubview(hostingController.view)
hostingController.view.translatesAutoresizingMaskIntoConstraints = false
let constraints = [
hostingController.view.topAnchor.constraint(equalTo: headerViewMain.topAnchor),
hostingController.view.leftAnchor.constraint(equalTo: headerViewMain.leftAnchor),
headerViewMain.bottomAnchor.constraint(equalTo: hostingController.view.bottomAnchor),
headerViewMain.rightAnchor.constraint(equalTo: hostingController.view.rightAnchor)
]
NSLayoutConstraint.activate(constraints)
headerViewMain.frame.size.height = headerViewMain.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize).height
headerViewMain.frame.size.width = headerViewMain.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize).width
self.tableHeaderView = headerViewMain
self.layoutIfNeeded()
self.setNeedsLayout()
self.reloadData()
}

Related

Duplicating UIViews in StackView when scrolling TableView Swift

Issue: When I scroll the tableView, my configureCell() method appends too many views to the stackView inside the cell.
When the cell is first displayed, and I press the UIButton, the stack is unhidden and first shows the right amount of views, after scrolling, the amount is duplicated.
prepareForReuse() is empty right now. I want to keep the stackView unHidden after scrolling.
I set the heightAnchor for the UIView as it is programmatic layout.
Expected Outcome: User taps on the button, the cell stackView is unhidden and the cell expands to chow the uiviews related to the cell.
When I call it in the cellForRowAt, nothing happens. Because im not sure how to modify the method for IndexPath.row.
protocol DataDelegate: AnyObject {
func displayDataFor(_ cell: TableViewCell)
}
class TableViewCell: UITableViewCell {
#IBOutlet weak var stackView: UIStackView! {
didSet {
stackView.isHidden = true
}
}
#IBOutlet button: UIButton!
var model = Model()
var detailBool: Bool = false
#IBAction func action(_ sender: Any) {
self.claimsDelegate?.displayClaimsFor(self)
detailBool.toggle()
}
func configureCellFrom(model: Model) {
if let addedData = model.addedData {
if addedData.count > 1 {
for data in addedData {
let dataView = DataView()
dataView.heightAnchor.constraint(equalToConstant: 65).isActive = true
dataView.dataNumber.text = data.authDataNumber
self.stackView.addArrangedSubview(dataView)
}
}
}
}
}
How would I call this in cellForRowAt, so its only created the correct amount of uiviews and not constantly adding?
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: TableViewCell.identifier, for: indexPath) as? TableViewCell else {
return UITableViewCell()
}
cell.dataDelegate = self
cell.configureCellFrom(model: model[indexPath.row])
//if let addedData = model.addedData {
//if addedData.count > 1 {
//for data in addedData {
//let dataView = DataView()
//dataView.heightAnchor.constraint(equalToConstant: 65).isActive = true
//dataView.dataNumber.text = data.authDataNumber
//self.stackView.addArrangedSubview(dataView)
// }}}
cell.layoutIfNeeded()
cell.selectionStyle = .none
return cell
}
extension ViewController: DataDelegate {
func displayDataFor(_ cell: TableViewCell) {
if tableView.indexPath(for: cell) != nil {
switch cell.detailBool {
case true:
UIView.performWithoutAnimation {
tableView.beginUpdates()
cell.detailArrow.transform = CGAffineTransform(rotationAngle:.pi)
cell.stackView.isHidden = false
tableView.endUpdates()
}
case false:
UIView.performWithoutAnimation {
tableView.beginUpdates()
cell.stackView.isHidden = true
cell.detailArrow.transform = CGAffineTransform.identity
tableView.endUpdates()
}
}
}
}
}
If I understand correctly, you would like to create an expandable TableView? If yes you can do it a lot of different ways, but you have to change your approach totally. Please refer LBTA approach:
LBTA video
My favourite the Struct approach, where you create a struct and you can save the complication with the 2D array:
Struct stackoverflow link

Swift - saveContext(), tableView, scrollView, and reloadRows issue?

I have tableView that uses a NSFetchedResultsController to populate data. When clicking on a cell, it takes you to a detailViewController of that object. And the following two properties are pushed forward with prepare(for:).
var coreDataStack: CoreDataStack!
var selectedGlaze: Glaze?
Inside the detailView, I have 2 cells. The first is cell that contains a scrollView with an array of images:
import UIKit
protocol SwipedRecipeImageViewDelegate: class {
func recipeImageViewSwiped(_ cell: RecipePhotoTableViewCell, selectInt: Int)
}
class RecipePhotoTableViewCell: UITableViewCell, UIScrollViewDelegate {
#IBOutlet weak var scrollView: UIScrollView!
#IBOutlet weak var pageControl: UIPageControl!
// -
var imagesArray: [Data] = []
var selectedImageData: Int = 0
// -
weak var delegate: SwipedRecipeImageViewDelegate?
// -
override func awakeFromNib() {
super.awakeFromNib()
self.backgroundColor = .clear
self.selectionStyle = .none
scrollView.delegate = self
scrollView.isUserInteractionEnabled = false // Allows didSelectAtRow:
contentView.addGestureRecognizer(scrollView.panGestureRecognizer) // Allows Scrolling
}
override func layoutSubviews() {
super.layoutSubviews()
setImages()
setOffsetX(pageNumber: selectedImageData)
}
func configureCell(section: Int, row: Int, images: [RecipeImage], arrayInt: Int, delegate: SwipedRecipeImageViewDelegate) {
self.delegate = delegate
selectedImageData = arrayInt
for image in images {
guard let imageData = image.recipeImageData else { return }
imagesArray.append(imageData)
}
}
#IBAction func pageChanged(_ sender: UIPageControl) {
setOffsetX(pageNumber: sender.currentPage)
}
func setOffsetX(pageNumber: Int) {
pageControl.currentPage = pageNumber
let offsetX = contentView.bounds.width * CGFloat(pageNumber)
DispatchQueue.main.async {
UIView.animate(withDuration: 0.2, delay: 0, options: UIView.AnimationOptions.curveEaseOut, animations: {
self.scrollView.contentOffset.x = offsetX
}, completion: nil)
}
}
func setImages() {
// Set Page Count:
pageControl.numberOfPages = imagesArray.count
// Set Frame For ImageViews + Scroll View:
for index in 0..<imagesArray.count {
let imageView = UIImageView()
imageView.frame.size = contentView.bounds.size
imageView.frame.origin.x = contentView.bounds.width * CGFloat(index)
imageView.image = UIImage(data: imagesArray[index])
imageView.contentMode = .scaleAspectFill
imageView.layer.masksToBounds = true // Limits Frame Size
scrollView.addSubview(imageView)
}
// Set ScrollView Size:
scrollView.contentSize = CGSize(width: (contentView.bounds.width * CGFloat(imagesArray.count)), height: contentView.bounds.height)
scrollView.delegate = self
}
// Set Page Number:
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
let pageNumber = scrollView.contentOffset.x / scrollView.frame.size.width
self.pageControl.currentPage = Int(pageNumber)
delegate?.recipeImageViewSwiped(self, selectInt: pageControl.currentPage)
}
The second cell contains a stackView with some labels to display data that the image shows. It accepts a lot of parameters and then sets the textColor and changes some labels. Nothing too exciting so I didn't include the code.
DetailViewController:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
print("cellForRowAt: ", indexPath)
switch indexPath.section {
case sectionImage: // Section 0:
guard
let images = selectedGlaze?.glazeImage,
let glazeImageSelected = selectedGlaze?.glazeImageSelected // This is a Double
else { return returnDefaultCell() }
let imageArray = images.allObjects as! [RecipeImage] // Takes NSSet of relational data and changes it into an Array to be passed into the image cell.
let imageSelected = Int(glazeImageSelected) // Double Converted to Int
switch indexPath.row {
case 0:
let cell = returnRecipeImageCell()
return configureRecipeImageCell(cell: cell, for: indexPath, imagesArray: imageArray, imageSelected: imageSelected)
case 1:
let cell = returnAtmosphereCell()
return configureAtmosphereCell(cell: cell, for: indexPath, imagesArray: imageArray, imageSelected: imageSelected)
default: return returnDefaultCell()
}
}
}
SwipedRecipeImageViewDelegate:
func recipeImageViewSwiped(_ cell: RecipePhotoTableViewCell, selectInt: Int) {
selectedGlaze?.glazeImageSelected = Double(selectInt)
coreDataStack.saveContext()
DispatchQueue.main.async { //
self.tableView.beginUpdates() //
let row1: IndexPath = [0,1] //
self.tableView.reloadRows(at: [row1], with: .automatic) //
self.tableView.endUpdates() //
}
}
The Issue:
The issue I'm having is reloading the second cell to be updated with the correct information after the recipeImageViewSwiped() is called. Seen here: https://imgur.com/a/fIYfehf
This happens when the code inside the DispatchQueue.main.async block is active. When the block is comment out, this happens: https://imgur.com/a/fYUVZKH - Which is what I'd expect. (Other than the cell at [0,1] isn't updated).
Specifically, when the tableView reloads row [0,1], cellForRowAt() only gets called on that row, [0,1]. But I'm not sure why the cell at [0,0], with the image, flicks back to the original image shown in the scrollView.
Goal:
My goal is to have the cell with the scrollView not flicker after being swiped on. But also to save the context, so that the object can save which image in the array is selected. And then to update/reload the second cell with the new information the image that's selected, so it can update it's labels correctly.
EDIT:
Removing the following in layoutSubviews() has this affect: https://imgur.com/a/vwrZfus - Which looks like it's mostly working. But still has a strange animation.
override func layoutSubviews() {
super.layoutSubviews()
setImages()
// setOffsetX(pageNumber: selectedImageData)
}
EDIT 2:
This looks like its entirely an issue with setting up the cell's view. Along with layout Subviews.
EDIT 3:
I added a Bool: hasSetLayout and a switch inside of layoutSubviews() and it appears to be working as I want. - However if any one still has any information to help me understand this issue, I'd appreciate it.
var hasSetLayout = false
override func layoutSubviews() {
super.layoutSubviews()
switch hasSetLayout {
case false: setImages(selectedPhoto: selectedImageData)
default: break
}
}
try to reload row without animation :
UIView.performWithoutAnimation {
tableView.reloadRows(at: [indexPath], with: .none)
}

Swift - show a complex View in UITableViewController with empty list through Storyboard

I'd need to show a View with some UIImageView, texts and links in my TableViewController if list controller is empty.
I know you can insert a label through this code:
func numberOfSections(in tableView: UITableView) -> Int
{
var numOfSections: Int = 0
if youHaveData
{
tableView.separatorStyle = .singleLine
numOfSections = 1
tableView.backgroundView = nil
}
else
{
let noDataLabel: UILabel = UILabel(frame: CGRect(x: 0, y: 0, width: tableView.bounds.size.width, height: tableView.bounds.size.height))
noDataLabel.text = "No data available"
noDataLabel.textColor = UIColor.black
noDataLabel.textAlignment = .center
tableView.backgroundView = noDataLabel
tableView.separatorStyle = .none
}
return numOfSections
}
But I'd need to show a complex View designed in the Storyboard in a different ViewController.
I tried with this:
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let emptyViewController = storyboard.instantiateViewController(withIdentifier :"empty_controller") as! EmptyViewController
tableView.backgroundView = emptyViewController.emptyView
Where emptyView is defined in this way
class EmptyViewController: UIViewController {
#IBOutlet weak var emptyView: UIView?
...
}
But it doesn't work, I can't see that view in the table if the list is empty.
How can I do it?
Thank you a lot
Best bet is to use two containers view one with tableview and one with emptyVC, then in parent of that container you can hide/unhide or remove/add view depending upon your logic
EmptyViewController has its own view, why you have to create another emptyView?
Remote emptyView property and use this code:
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let emptyViewController = storyboard.instantiateViewController(withIdentifier :"empty_controller") as! EmptyViewController
tableView.backgroundView = emptyViewController.view
Set you view controller to inherit from UITableViewDelegate and set the tableView.delegate to point to your VC.
class VC : UITableViewController, UITableViewDelegate {
override func viewDidLoad() {
...
tableView.delegate = self
}
}
Then override the following two functions
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
}
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
}
This'll add a header to your tableview section. You can edit what this header is based on the contents of the table

Tab Bar Item hidden behind tableview / not being shown?

I have an empty view with a tab bar pictured below, when i load a routine a table appears containing the contents, however it seems to overlay the tab bar killing off app navigation. Its not sized in the storyboard to overlay it and its constraint locked to not do so, so im unsure why this is happening, pics of the issue and VC's code below:
VC Code:
import Foundation
import UIKit
import CoreData
class RoutineController: UIViewController, UITableViewDataSource, UITableViewDelegate {
// MARK: - DECLARATIONS
#IBAction func unwindToRoutine(segue: UIStoryboardSegue) {}
#IBOutlet weak var daysRoutineTable: UITableView!
#IBOutlet weak var columnHeaderBanner: UIView!
#IBOutlet weak var todaysRoutineNavBar: UINavigationBar!
#IBOutlet weak var addTOdaysRoutineLabel: UILabel!
let date = Date()
let dateFormatter = DateFormatter()
let segueEditUserExerciseViewController = "editExerciseInRoutineSegue"
//This is the selected routine passed from the previous VC
var selectedroutine : UserRoutine?
// MARK: - VIEWDIDLOAD
override func viewDidLoad() {
super.viewDidLoad()
setupView()
daysRoutineTable.delegate = self
daysRoutineTable.dataSource = self
view.backgroundColor = (UIColor.customBackgroundGraphite())
dateFormatter.dateStyle = .short
dateFormatter.dateFormat = "dd/MM/yyyy"
let dateStr = dateFormatter.string(from: date)
todaysRoutineNavBar.topItem?.title = dateStr + " Routine"
}
// MARK: - VIEWDIDAPPEAR
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.daysRoutineTable.reloadData()
self.updateView()
}
// MARK: - TABLE UPDATE COMPONENTS
private func setupView() {
updateView()
}
// MARK: - TABLE SETUP
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if let count = self.selectedroutine?.userexercises?.count
{
print("exercises: \(count)")
return count
}
return 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as? TodaysRoutineTableViewCell else {
fatalError("Unexpected Index Path")
}
cell.backgroundColor = UIColor.customBackgroundGraphite()
cell.textLabel?.textColor = UIColor.white
configure(cell, at: indexPath)
return cell
}
// MARK: - VIEW CONTROLER ELEMENTS VISIBILITY CONTROL
fileprivate func updateView() {
var hasUserExercises = false
if let UserExercise = self.selectedroutine?.userexercises {
hasUserExercises = UserExercise.count > 0
}
addTOdaysRoutineLabel.isHidden = hasUserExercises
columnHeaderBanner.isHidden = !hasUserExercises
daysRoutineTable.isHidden = !hasUserExercises
}
// MARK: - SETTING DATA FOR A TABLE CELL
func configure(_ cell: TodaysRoutineTableViewCell, at indexPath: IndexPath) {
if let userExercise = selectedroutine?.userexercises?.allObjects[indexPath.row]
{
print("\((userExercise as! UserExercise).name)")
cell.todaysExerciseNameLabel.text = (userExercise as! UserExercise).name
cell.todaysExerciseRepsLabel.text = String((userExercise as! UserExercise).reps)
cell.todaysExerciseSetsLabel.text = String((userExercise as! UserExercise).sets)
cell.todaysExerciseWeightLabel.text = String((userExercise as! UserExercise).weight)
}
}
}
requested table constraints
Debug hierarchy
The Segue that sends the user back to the view that looses its tab bar
if segue.identifier == "addToTodaySegue" {
let indexPath = workoutTemplateTable.indexPathForSelectedRow
let selectedRow = indexPath?.row
print("selected row\(selectedRow)")
if let selectedRoutine = self.fetchedResultsController.fetchedObjects?[selectedRow!]
{
if let todaysRoutineController = segue.destination as? RoutineController {
todaysRoutineController.selectedroutine = selectedRoutine
}
}
}
I also feel perhaps the viewDidAppear code may cause the issue, perhaps the super class?
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.daysRoutineTable.reloadData()
self.updateView()
Updated storyboard image
I suspect you need to embed your viewController in a UINavigationController.
Consider the following setup:
I suspect your setup is like the upper one:
TapBar -> ViewController -show segue-> ViewController
Which results in a hidden tapbar, like in your description:
While the bottom setup:
TapBar -> NavigationCntroller -rootView-> ViewController -show segue-> ViewController
results in:
which is what you want, how I understood.
Update
It's hard to see. The screenshot of your Storyboard is in pretty low resulution, but the segues look wrong. Double check them. A Segue of type show (e.g push) looks like this:
Also clear project and derived data. Segue type changes sometime are ignored until doing so.
Try calling this self.view.bringSubviewToFront(YourTabControl).
The previous suggestion should work. But the content at the bottom part of tableview will not be visible as the tabbar comes over it. So set the bottom constraint of tableview as the height of tabbar.

UIView is always behind UITableView

I have a UIViewController (not a UITableViewController because I read that a view controller is best for this kind of behaviour) with a UITableView. In this view controller I want to add a floating UIView and place it above my tableview, to do so I wrote:
public override func viewDidLoad() {
super.viewDidLoad()
// add button
let fbv = liquidActionButtonInstance.addActionButton() // this is a UIView
self.view.addSubview(fbv)
liquidActionButtonInstance.delegate = self
// delegate
tableView.delegate = self
tableView.dataSource = self
}
However my floating view appears behind my UITableView, how can I add it as the first child of self.view? I've used
self.view.addSubview(fbv)
self.view.bringSubviewToFront(fbv)
Among others and none seems to work.
Edit:
I added some screenshots of my view's hierarchy.
Edit 2:
Here I put a little more code:
My ViewController without some unrelated code:
public class ActividadesTableViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var descripcionFiltrosLabel: UILabel!
#IBOutlet weak var filtrosLabelBottomConstraint: NSLayoutConstraint!
#IBOutlet weak var filtrosLabelTopConstraint: NSLayoutConstraint!
#IBOutlet weak var tableViewTopConstraint: NSLayoutConstraint!
private var liquidActionButtonInstance: FloatingActionButton = FloatingActionButton()
public var viewModel : ActividadesTableViewModeling?
public override func viewDidLoad() {
super.viewDidLoad()
// Agregar action button
self.view.insertSubview(liquidActionButtonInstance.addActionButton(),aboveSubview: tableView)
liquidActionButtonInstance.delegate = self
// set row's height
tableView.estimatedRowHeight = 70
tableView.rowHeight = UITableViewAutomaticDimension
// delegate
tableView.delegate = self
tableView.dataSource = self
// load tableview data
if let viewModel = viewModel {
viewModel.loadActividades(withFilters: nil)
}
}
}
// MARK: FloatingButton
extension ActividadesTableViewController: FloatingActionButtonDelegate {
public func performSegueFromFloatingActionButton(segueName name: String) {
performSegueWithIdentifier(name, sender: self)
}
}
// MARK: TableView
extension ActividadesTableViewController: UITableViewDataSource, UITableViewDelegate {
public func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Omitting this code, just mentioning the methods
}
public func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("ActividadCell", forIndexPath: indexPath) as! ActividadTableViewCell
if let viewModel = viewModel {
cell.viewModel = viewModel.cellModels.value[indexPath.row]
} else {
cell.viewModel = nil
}
return cell
}
public func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
}
}
And this is how I add the button (in a different class):
func addActionButton() -> LiquidFloatingActionButton {
let createButton: (CGRect, LiquidFloatingActionButtonAnimateStyle) -> LiquidFloatingActionButton = { (frame, style) in
let floatingActionButton = LiquidFloatingActionButton(frame: frame)
floatingActionButton.animateStyle = style
floatingActionButton.dataSource = self
floatingActionButton.delegate = self
floatingActionButton.color = ColoresKairos.principal2
return floatingActionButton
}
let cellFactory: (String) -> LiquidFloatingCell = { (iconName) in
return LiquidFloatingCell(icon: UIImage(named: iconName)!)
}
cells.append(cellFactory("iphone-action-button-group"))
cells.append(cellFactory("iphone-action-button-notepad"))
cells.append(cellFactory("iphone-action-button-check-box"))
let floatingFrame = CGRect(x: UIScreen.mainScreen().bounds.width - 56 - 16, y: UIScreen.mainScreen().bounds.height - 56 - 16, width: 56, height: 56)
let bottomRightButton = createButton(floatingFrame, .Up)
//view.addSubview(bottomRightButton)
//return view
return bottomRightButton
}
You need to add the view using addSubview(_:) AND bringToFront(_:). You can also try sending the tableView to the back using sendToBack(_:)
In your document outline menu in your storyboard, you can place your floating view below your TableView in view hierarchy. Then, you can always see your floating view placed above your tableview.
It's gonna be like this.
▼ Your ViewController
Top Layout Guide
Bottom Layout Guide
▼View
▶︎ TableView
▶︎ Floating View
Below is the code that is working along with its screenshot, I suspect that your call liquidActionButtonInstance.addActionButton() returns an empty button?
class ViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
private var liquidButton: LiquidFloatingActionButton?
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
// Setting up the liquid button
liquidButton = createLiquidButton()
view.addSubview(liquidButton!)
}
func createLiquidButton() -> LiquidFloatingActionButton {
let frame = CGRect(x: UIScreen.mainScreen().bounds.width - 56 - 16, y: UIScreen.mainScreen().bounds.height - 56 - 16, width: 56, height: 56)
let button = LiquidFloatingActionButton(frame: frame)
button.animateStyle = .Up
button.color = UIColor.redColor()
return button
}
}
extension ViewController: UITableViewDelegate, UITableViewDataSource {
// UITableView Implementation, not included not relevent for the example.
}
The error here, after debugging the view hierarchy was that my tableView wasn't being added directly to the UIViewController's hierarchy, so it was added above everything else, and adding my FloatingButton as a subview of UIViewController always resulted on it being hidden by my tableView.
My tableView is added via storyboard, and as far as I know this is not the expected behaviour, but in my case just adding the following lines:
self.view.addSubview(tableView)
self.view.addSubview(liquidActionButtonInstance.addActionButton())
Solved my problem.