I've read here that if I have UITextField in my table cells, the table scrolls automatically to the cell that contains them. This works perfectly.
Making a UITableView scroll when text field is selected
Is there a way to get this to work with a UIViewController? I'm using a UIViewController because I have a lot of custom UI that needs to be laid out on top of the table view. Thanks!
I would recommend splitting your viewController into several using containerViews.
That way, you can use a UITableViewController for your tableView and use a regular UIViewController for all the custom UI.
Another way would be to set a scrollView's contentOffset manually when a textField is in focus.
It would look something like that.
#IBAction func textField(didBeginEditing sender: UITextField) {
let point = CGPoint(x: 0, y: textField.frame.minY)
scrollView.setContentOffset(point, animated: true)
}
Related
I am neither an iOS developer, nor a swift developer, but please bear with me:
I am currently trying to implement a simple iOS app but I have difficulties understanding how exactly I am supposed to set up custom UIViews and ViewControllers for those UIViews.
I am using a UIScrollView that is containing items a little bit more complex than just images, thats what I use custom views for.
What I did was:
I created a .xib file, the view itself. I added some elements (here it is only a textfield, for simplicity's sake).
I created a cocoa touch class "CustomView" that inherits from UIView and set my view up to be of that class (inside the class I just set up elements and such).
Now I want a ViewController that controls the class whenever it is rendered (for example reacting to the changing textField).
I cant manage everything from my main ViewController, because it would get too big (e.g. 3 scrollViews * 5 subviews that need to be managed).
I want a solution that uses ViewControllers for each subview (in case they themselves will have subviews, too).
How do I do that?
Do I need to add some sort of childViewController?
I really am at loss, most of the blog posts and SO examples simply do not work and/or are outdated and I am unsure about whether or not I got the whole View - ViewController pattern wrong.
Let's say you have two view controllers, MainViewController and TableViewController. TableVC's main view is to be a subview of MainVC's main view. In addition, you wish to pass back to MainVC which cell was selected in TableVC.
A solution is (a) make TableVC be a child to MainVC and (b) make MainVC be a delegate for TableVC.
TableViewController:
protocol TableVCDelegate {
func cellSelected(sender: TableViewController)
}
class TableViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
// please note that you can do delegation differently,
// this way results in crashes if delegate is nil!
var delegate:TableVCDelegate! = nil
var someValue = ""
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// set someValue to contents in the selected cell or it's data source
someValue = "Hello World!"
delegate.cellSelected(sender: self)
}
}
MainViewController:
class MainViewController: UIViewController, TableVCDelegate {
let tableVC = TableViewController()
override func viewDidLoad() {
// make tableVC be a child of this VC
addChild(tableVC)
tableVC.didMove(toParent: self)
tableVC.delegate = self
// position tableVC.view
tableVC.view.translatesAutoresizingMaskIntoConstraints = false
}
func cellSelected(sender: TableViewController) {
print(sender.someValue) // this should send "Hello World!" to the console
}
}
This is obviously untested code, but it is based on product code. This is meant to be a shell to help you get started.
I have a view controller with a tableview containing a list of chats, a search controller enbeded in the navigation item (iOS 11 feature)
let searchController = UISearchController(searchResultsController: nil)
searchController.dimsBackgroundDuringPresentation = false
navigationItem.searchController = searchController
definesPresentationContext = true
When the user taps a chat in the table view the app pushes a new view controller with another table view containing messages for that chat. That works like it is supposed to:
The problem is that when the user activates the search controller, find some chat and taps it, the pushed view controller containing table view with the chat messages does some really strange animation with the table view that should not happen:
I load the data before the actual navigation and bind it to the table view in viewDidLoad using just reload() on the table view. The problematic table view uses auto layout and custom cells.
The problem is very similar to UITableView has unwanted animation when reloadData is called but for me it only happens when the iOS 11 search controller is active.
Edit: If I remove tableView.rowHeight = UITableViewAutomaticDimension and use a fixed height using func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat the problem is still there
If you just hide the searchBar before pushing a new viewController then it may fix your problem.
You need to create a global variable for searchBarCancelButton and find the cancel button from its subviews when you search something
let buttons = searchController.searchBar.subviews.first?.subviews.filter { (view) -> Bool in
return NSStringFromClass(view.classForCoder) == "UINavigationButton"
} as? [UIButton]
searchBarCancelButton = buttons?.first
then you can manually cancel it.
self.searchBarCancelButton?.sendActions(for: .touchUpInside)
Personally, I would simply hide the searchView controller before presenting the new view controller.
( Using UIView.animates with a completion handler for example )
I would not try to investigate further because since iOS11, there is an esoteric problem in the safe area management. A bug ? :)
Even the launch screens layouts are not correctly handled.
So many majors logos miss their middle part at launch !
You can try to call cell.layoutIfNeeded() right after dequeuing and setting a content of the cell
iOS 11 completely revamped the safe area API, including scroll view inset adjustment behaviors, which can cause unwanted animation when ignored. Therefore, disable automatic content inset adjustment for the scroll view with the unwanted animation:
if #available(iOS 11.0, *) {
tableView.contentInsetAdjustmentBehavior = .never
} else {
// < iOS 11 logic
}
Do not invoke reloadData() method in your viewDidLoad or viewWillAppear. Instead of this reload your tableView with empty data in viewDidLoad so your tableView will show nothing and then in your viewDidAppear invoke reloadData() method to load all of your chats. This will restrict your tableView from loading with unwanted animation.
var shouldShowEmpty = true
func viewDidLoad() {
super.viewDidLoad()
tableView.reloadData()
}
func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
shouldShowEmpty = false
tableView.reloadData()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if shouldShowEmpty {
return 0
}
return yourArray.count
}
As per attachment, It looks like due to auto layout, cell's element's height was zero (UILabel or UIImageView) and then suddenly when table view reloads it gets the data inside the cell which increase its height(automatic dimension or whatever) which cause this animation.
Try to push it without animation or try to set fix cell's element's height and width and check is it still showing this animation.
Did you check without invoking UISearchBar, If you select on any cell same animation is happening ?? or did you try to remove UISearchbar and select on the cell and check the animation part ?
Please share your code so we can see more clearly.
I have had a similar problem, and I believe that solution is the same. The keyboard is causing the problem, to be more correct, keyboardWillHideNotification trigger. You have text field at the bottom, that probably listens to notifications for keyboard show/hide, where layoutIfNeeded() is triggered if you animate bottom constraint so that your keyboard doesn't overlap your text field. So when you finish your search in the search text field, keyboardWillHideNotification gets triggered at an unwanted time. I solved my issue by calling:
resignFirstResponder()
for the text field that causes this event. In my case, that was after pressing the button, in your, I believe it's in didSelect tableView cell in the search table view.
If you are still active at this issue, please let me know if you manage to solve it. I broke my head trying to solve this issue, which apparently is so simple and straightforward.
In VC2 Try a delay function before reloading table
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
tableView.reloadData
}
or else
In VC1 in didSelect resign first responder of search controller before pushing to VC2
or
In VC1 in didSelect resign first responder of search controller and set a delay before pushing to VC2.
I am having trouble on performing a segue when you click on a tableView's cell. I am not using storyboard at all in the application (deleted the storyboard file), and was wondering how I could perform this segue.
Note:
My tableView is in a different file than the viewController that displays it. So in my ViewController i have this line:
let tableView = MyTableView(restaurants_data: restaurants, frame: CGRect(), style: .Plain)
view.addSubview(tableView)
So since MyTableView.swift is the file where the didSelectRowAtIndexPath function is located at I cant do something like
self.navigationController?.showViewController(destinationVC, sender:nil)
So, how do I perform a segue when the user clicks on the cell, given that I am using a custom TableView and my application is not using StoryBoard?
Thanks a lot in advance
Add a reference for the viewController inside your tableView
and initialise it in the viewController init
var controller : UIViewController
controller.presentViewController(viewControllerToPresent: UIViewController, animated: Bool, completion: block?>)
I would like to present a view that only occupies the bottom half of the screen, with the background view remaining visible with no possible interaction.
Just like an UIAlertContoller with the actionSheet style.
How to do that in Swift ?
Create that view as a normal UIViewController, assign an identifier to it and then instantiate it like this: (keep it as a member variable though, you will need it)
var controller = self.storyboard!.instantiateViewControllerWithIdentifier("yourIdentifier") as! UIViewController
then you can add it to your parent view like
self.addSubview(controller)
Be careful about positioning it though, you can animate it neatly to the middle of the screen like
UIView.animateWithDuration(0.5, animations: {
controller.frame.origin.y = UIScreen.mainScreen().bounds.height/2
})
in
But the initial positioning should be made the holding view's viewDidLayoutSubviews method, like
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
controller.frame.origin.y = UIScreen.mainScreen().bounds.height
}
because in viewDidLoad the views don't really have correct frames yet and it would end up in wrong position
I have a static table view with UITableViewController. I add UIImageView to first cell and connect this UIImageView outlet to my custom UITableViewController.
Inside viewWillAppear after super.viewWillAppear(animated) I try to set image for image view, but it looks hidden. Only if I do this inside viewDidAppear my image is shown. I have no implemented methods from UITableViewDelegate/DataSource and my image view is always not hidden. If I call reloadData() nothing happens (it's useless for static tables).
I want my image set before viewDidAppear, so I don't want to see delay before my image is set.
class FPProfileViewController: UITableViewController {
#IBOutlet weak var userPhotoImageView: UIImageView!
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
userPhotoImageView.image = //myImage
}
}
It looks strange, in my other project the same algorithm works fine.
I think this is what you're looking for, had the same problem. It seems UIImageView's in a static table view cell need to be part of - (void) viewWillAppear - based on this answer : Static cells: Content does not appear.
For my situation, it didn't help, I had to go straight in - (void) viewDidLoad.