What is the best approach for attaching a UIButton on top of UIScrollView or UITableView so when the view is scrolled, the button stays in its place.
Here examples below:
UIButton stays in the right bottom corner when the view is scrolled.
google+ app example
yahoo mail app example
I think this should work. Lay Out your button in a view that is outside of the tableviewcontroller. Then drag an outlet to the tableviewcontroller file. Then add it in code. This code would hold it at the top of the screen.
#IBOutlet var buttonView: UIView!
override func viewDidLayoutSubviews() {
self.view.addSubview(buttonView)
}
override func scrollViewDidScroll(scrollView: UIScrollView) {
var rect = self.buttonView.frame
rect.origin.y = max(0,scrollView.contentOffset.y + scrollView.contentInset.top)
self.buttonView.frame = rect
}
Thank you all for great answers!
I got it worked through storyboard by moving the button from scrollView to View itself. That way it's attached on UIView and it's independent of scrollview.
storyboard snapshot
So now the structure is:
- View
- ScrollView
- Button
Before it was:
- View
- ScrollView
- Button
There are many ways to go about doing this but two that I use most often are as follows.
One approach is embedding the view controller within a navigation controller. This will set a bar on the top and bottom if you choose that you can place bar button items upon.
Another approach is to place a UIView along the top and snap the constraints to the left, right, and top with 0 no-margin. Then set the height. I usually use 40px for the height but you can use what is applicable to your needs. After that you can place a button in that UIView and then set constraints on it to keep in in place.
In my experience, this isn't reliably possible to do with the scrollView itself.
My solution is usually to put anything that needs to float above the tableView/scrollView in a plain ViewController that also contains the tableView/scrollView parent.
If you're using storyboards with a UITableViewController scene, this will likely mean you need to use another scene with UIViewController with a container that has your UITableViewController.
For UITableView use tableHeaderView. For UIScrollView you need to create a separate view not in the scroll view's hierarchy.
Another solution is to put your UIButton in a UIToolbar, and then make the toolbar a child of the UINavigationController's view. After that, in viewDidLayoutSubviews, you can set the rect of the toolbar to sit just below the navigation bar and offset the top of the UIScrollView or UITableView.
Add button which you want in the storyboard.
Design your scrollview
self.view.sendSubviewToBack(scrollViewObj)(in the code)
This worked for me.
Related
I am designing an iOS app in swift, and I am having some difficulty with animations during a controller transition. Specifically, I've implemented a UINavigationControllerDelegate, to listen for when a certain view is pushed. When this view is pushed, I want to hide a bar at the bottom of the screen. My code is working almost perfectly, however whenever I begin an animation on the height of the navigation controller, the current view (which is being removed) animates its height correctly, but the new controller which is being pushed already has the new height from the animation. To put some code to it, the following function is called from my UINavigationControllerDelegate's willShow viewController function:
func animatePlayerVisibility(_ visible: Bool) {
if visible == showingPlayer {
return
}
showingPlayer = visible
let height: CGFloat = visible ? 56.0 : 0.0
self.view.layoutIfNeeded()
UIView.animate(withDuration: 0.35) {
self.playerHeight.constant = height
self.viewBottom.constant = height
self.view.layoutIfNeeded()
}
}
'playerHeight' is an IBOutlet to a constraint on the height of the player container view. 'viewBottom' is also an IBOutlet constraint between the bottom of the top container view and the bottom of the screen. Essentially, as long as these two constraints are animated together, it should look nice.
To help visualize the graphical bug, I edited this line
self.viewBottom.constant = height
to
self.viewBottom.constant = height * 2.0
I have created an imgur album of the actual wrong behavior in action:
http://imgur.com/a/znAim
As you can see, the old view controller animates properly, when the new controller already has the new animated size.
Here is the layout of my storyboard:
Any help would be really appreciated. I've been trying to fix this for a while with no success.
EDIT: The view of the animation without the *2 applied.
https://imgur.com/a/2a5Sw
Have you thought about not using UINavigationController? Maybe it will be easier to use ChildViewControllers mechanism. Then with it you can use a powerful autolayouts and have more control over animation (in your case height)
More info about this here
I've created a nice little sample project you can find here!
There are a number of things that could be going wrong, and since I haven't looked over your project personally it's likely I organized things very differently in my sample, but hopefully you will understand it. I think the big thing is that I added a constraint in storyboard to the navigationController's container to the bottom of the root viewController. I don't adjust the height of this container at all when I animate.
I have the following UIViewController which i am doing in story board.
The problem i am facing here is that when i try to swipe right on tv remote my UIImageView does not get any focus. Infact no focus event happen, it try to put some logs in
override func didUpdateFocusInContext(context: UIFocusUpdateContext, withAnimationCoordinator coordinator: UIFocusAnimationCoordinator)
but there is no focus event updated. Earlier i have UIImageView directly added on self.view in storyboard but i read somewhere the UIImageView itself is not focusable so i put my UIImageView inside another UIView, but it still does not gets any focus. I have aligned top edges of UIView which contains UIImageView and UITextView.
Any help will be highly appreciated
On a side note i also tried to do with UIFocusGuide but that didn't work also. I think i don't need to use UIFocusGuide because it a basic right swipe focus thing.
UIImageView is not by default focusable, and will not get focus effect, you can do two things to make it focusable.
Subclass UIImageView and in subclass override canBecameFocused and
return YES, and then override didUpdateFoucsInContext and update appearance for focus state, like if focus make it bigger or change color etc.
Second option is add it to a view which is focusable and then set imageView.adjustImageWhenAncestorFocused = YES also set clipToBounds = NO on superview
So In your case as you are adding imageView to a view then that view should return YES/True from canBecameFocused.
#C_X is right. The most important thing is that if UIImageView is added to self.view then don't forget to subclass self.view and override the canBecameFofused method.
I have a UIScrollView that contains a bunch of view controllers, and in each view controller there is a button that if pressed, should scroll the UIScrollView to a certain position. How do I go about connecting the viewController IBAction for the button to set the scroll position of the UIScrollView?
I know I'll call contentOffset at some point, but I'm struggling with getting the ViewControllers to control the Scroll View.
One way you could do it... When you add each UIViewController.view to the UIScrollView you could also set a var in each UIViewController that references the UIScrollView.
For example, for some UIViewController, we'll call it X, put
var scrollView: UIScrollView?
in it. Then in your class that controls the UIScrollView, when you add view X to the UIScrollView, also do the following:
viewControllerX.scrollView = self.theScrollView
Now, when your IBAction method is called in viewControllerX, you have a reference to the UIScrollView and as you said, can do self.scrollView.contentOffset.y = someValueHere to change its position.
I have a navigation setup where at the top there is a UITabBarController. I then have a tab, which is instantiated by creating a UIViewController placed into a UINavigationController like so:
UIViewController *testVC = [UIViewController new]; // Has UITableView as subview
UINavigationController *testNavVC = [[UINavigationController alloc] initWithRootViewController:testVC];
[self setViewControllers:#[testNavVC]];
The problem that arrises is with the UITableView inside the testVC UIViewController. The table displays properly at the top and is correctly situated underneath the UINavController's nav bar. When you scroll the table view to the bottom, however, the final rows in the table view will be cut off at the bottom of the screen. I found out that I can set the bottom content inset to 100(value will differ based on row height) to correctly display the content. I don't feel like I should need to do that though, and am looking for a better solution.
How can I properly add a UITableView that is nested in this way?
As a side note this all works correctly when using a UITableViewController rather than a UIViewController with the added UITableView. In my case I am needing to use the latter option.
You can try to adjust UITableView bottom inset without hardcoding, by using bottomLayoutGuide property:
tableView.contentInset = UIEdgeInsetsMake(0.0, 0.0, self.bottomLayoutGuide.length, 0.0);
It indicates lowest vertical extent for content, and can be used from iOS 7.
As an alternative you can create bottom NSLayoutConstraint for UITableView with this value.
All of my code was done programmatically and the problem ended up being that I setup the UITableView with the views frame. I switched it over to use autolayout instead and it worked great!
I'm trying to position a TableView inside my ViewController view but leaving a 44 height gap between the bottom of the navigation bar and the top of the table. I then wanted to place a UITextField inside that gap to act as a stationary header. For some reason, the TableView has an empty white space above the start of the "Prototype Cells". Its just white space. Here is what it looks like in the storyboard.
When viewing the app display, this is what it looks like:
When scrolling the table, it goes all the way up to the correct place:
Try to look in the 'attribute inspector' (in the right menu) of the Participants ViewController.
Check for the option 'Extend Edges' and uncheck the 'Under Top Bars', and then relocate your tableview.
Another possible solution is to just uncheck the option 'Adjust Scroll View Insets'.
The reason is that when the viewController extends its edges, let's say under the top bar, the tableView's scrollView automatically adjusts its inset top, so that the content of the tableView will start exactly under the top bar. But in your case this is not needed, since your tableView itself starts under the bar.
Focus on the ViewController and got to the Attribute Inspector tab:
Try this one:
self.tableView.contentInset = UIEdgeInsetsMake(-65, 0, 0, 0)
Just add this in you ViewDidLoad method
self.automaticallyAdjustsScrollViewInsets = NO;
change your table view style from grouped to plain
You should not need to change the default setting for Extend Edges.
Looks like the real problem is a blank table header view in your storyboard. It's showing in the screenshot you provided of your storyboard, right below the Enter Name view and right above the Prototype Cells view. Delete it.
My issues is, I set tableHeaderView as new UIView like this
self.tableHeaderView = UIView(frame: .zero)
Remove this line, the issue is gone. Try this:
//self.tableHeaderView = UIView(frame: .zero)
This is the 2022, iOS 15 solution if anyone stumbles upon this.
if #available(iOS 15.0, *) {
UITableView.appearance().sectionHeaderTopPadding = CGFloat(0)
}
I just found a solution for this.
In my case, i was using TabBarViewController, A just uncheck the option 'Adjust Scroll View Insets'. Issues goes away.
https://i.stack.imgur.com/qgQI8.png
For Objective-C folks, add this in your ViewDidLoad method.
[self.tableView setContentInset:UIEdgeInsetsMake(-20, 0, 0, 0)];