I have a collection view and a text field on a ui view.
Initially, I have a text field at the top of the ui view and then I set the collection view top Anchor constraint to the bottom of the text field.
I want to implement a behavior where if I scroll down the collection view, the text field should disappear and the collection view's top should be at the top of the same ui view (this would hide the text field). When I scroll up, I want the constraints to be like the initial ones (container view's top Anchor should be set to the bottom of the text field). Any hint at how I might implement this behavior? I was hoping to implement this by updating the constraints when scrolling up and scrolling down happens. How can I implement this?
UICollectionView subclasses UIScrollView, so you can implement a UIScrollViewDelegate to get the scroll events.
Here is some code I use in one of my apps
The bottom of the text field is constrained to the top of the collection view
The top of the text field is constrained to the view's top layout guide - This is the constraint that is modified by the scroll view delegate code
As the collection view scrolls up the text field will move up and fade out
extension ViewController: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let textHeight = self.textField.frame.size.height + 2
let offset = min(scrollView.contentOffset.y, textHeight)
if offset >= 0 || self.textFieldConstraint.constant != 0 {
self.textFieldConstraint.constant = -offset
let percent = max(1 - offset/textHeight,0)
self.textField.alpha = percent
}
}
}
Essentially you "push" the text field off the top of the screen, no more than its height+2
I think this solution can work full for you try this, just add a UIScrollView view as parent and put your text field and collection view in scroll view, as set the constraint as you set.
Related
I'm trying to create layout that it structured like this:
- View
-- ScrollView
--- ContentView
---- CustomView
---- CustomView
---- TableView
---- CustomView
The tableView itself is auto-resizable using "invalidateIntrinsicContentSize" and when I add items - the height of the tableview changes, pushing the custom view below it further down.
Once enough items are added I the bottom custom view is hidden and the scroll doesn't work.
important fact - the bottom custom view doesn't have a bottom constraint. It is pushed down by the it's top constraint to the tableView.
If I do set a bottom constraint - the table view will no longer be dynamically resized.
The intended behaviour:
When a user adds items to the list and the list gets too big the ContentView will be scrollable so the user can scroll to see the bottom view.
The actual behaviour:
When a user adds items to the list and the list gets too big, the bottom view is pushed down and outside of sight and content is not scrollable.
What is happening and how can I fix it?
Below is what I think what is happening.
Since you are using UITableView, it has its own scroll view. So when the UITableView list gets too big, UITableView itself becomes scrollable rather than ScrollView's contentView becoming scrollable.
To achieve what you need, you would have to make the UITableView not scrollable and use the intrinsicHeight of the UITableView to get the actual height of UITableView along with all the items. If you have items with varying heights, it will be a problem because you won't know the height before rendering. With same height for all the rows, you can get the total height of the UITableView and set the height constraint to that value. This will increase the contentSize of the outer ScrollView, making it scrollable.
Apart from UITableView, you can also use UIStackView. This is because you are not using the reusing capabilities of UITableView anyways. Managing the datasource and delegates should not be a big problem.
You can create a constraint for tableview height, And take its reference to your swift file, by dragging it as you take other views. Now in your code, Just do this
tableViewHeightConstraint.constant = tableViewNoOfItems * tableViewCellHeight;
if you have set other constraints perfectly inside scrollview, It should work perfectly. Means TableView should have top, bottom, left, right margined constraints from the ScrollView.
try this code
tblViewHeight.constant = CGFloat( tableview row count * 45 )
var size = contentView.systemLayoutSizeFitting(UILayoutFittingCompressedSize)
if size.height < scrollView.frame.size.height
{
size = scrollView.frame.size
}
contenViewHeight.constant = size.height - scrollView.frame.size.height
scrollView.contentSize.height = contenViewHeight.constant
What I think you could do is:
Disable tableView's scroll tableView.isScrollEnabled = false
Every time a user adds items to the list, reload the tableView
Also using UIStackView with vertical axis and .fillEqually distribution as a Content View would be much more convenient as you won't need to set any positional constraints to your views, but may need to set height constraints if intrinsic content size can't be determined by the engine
I want to hide the view above my table view when I scroll down and show it again when scrolling up. That kind of behavior is implemented in many apps. How can I manage to do that?
Hook the height constraint of the view above the table
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let viewY = scrollview.contentOffset.y
self.topViewH.constant = (viewY == 0) ? 200 : 0
self.view.layoutIfNeeded()
}
Problem: It is not recommended to put a tableView inside a scrollView (basically tableView itself is a scrollView). Both iOS and user will get confused how/where to scroll because of scroll inside a inside.
My solution: For your usecase, you can put your view (which you want to hide on scroll down and show on scroll up) in the tableview header itself.
I am trying to hide the search field by default in my app like seen in multiple Apple apps.
(Image credit: OS X Daily http://osxdaily.com/2017/07/27/search-notes-ios/)
Right now, the search field is on top of the contained view and it works fine. I could set a default offset to hide the search bar, but it won't prevent being shown when the scroll content size length is smaller than the scrollview.
By the way, I am NOT using a UITableView inside the UIScrollView, it's a custom view with subviews.
Hook the height constraint of you custom search view as IBOutlet and make this to hide it
self.searHeightcon.constant = 0
self.view.layoutIfNeeded()
Show
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if(scrollView.contentOffset.y == 0)
{
self.searHeightcon.constant = 50
}
else
{
self.searHeightcon.constant = 0
}
self.view.layoutIfNeeded()
}
If you always set the contentHeight to be same or larger than the scroll view’s height you can hide it by starting at an offset.
So I'm pretty puzzled right now because my UIScrollView is acting pretty weirdly.
Basically, I have a UIScrollView that is about twice the height of an iPhone 6 that I am using to display some graphs using iOS Charts and so I've set it to be able to scroll and bounce vertically, but neither scroll nor bounce horizontally. All of the graphs, and some additional UITextFields and UILabels are embedded on a separate "body view" that is aligned with the frame of the UIScrollView as seems to be common practice. Thus, the hierarchy of my views looks like this:
This worked well until I noticed today that when I press a specific UITextField on this UIScrollView, which triggers a UIPickerView, all of the sudden my scroll view starts to allow horizontal bouncing. This behavior does not occur for the two other UITextField's on the body view when they are tapped.
I've checked all of the code that is being triggered by tapping on the affected text field, and nothing is directly editing the frames or bounds of any UI objects. In fact, the only function called when the text field is tapped on is the textFieldDidBeginEditing. I've attached the code for this function below, but I am fairly certain it is not the problem.
My next suspicion was that the UIPickerView popping up has been messing with the dimensions of my scroll view and/or it's embedded view. I'm not quite sure if this is possible/probable, but this whole thing has left me pretty stumped.
Here's my code:
func textFieldDidBeginEditing(_ textField: UITextField) {
if textField == overallTimeframeTextField {
...
// Not the problematic text field
} else if textField == subjectTimeframeTextField {
...
// Also not the problematic text field
} else { // Affected text field
// Set the text of the text field
if textField.text == "" {
// This is executed in this scenario
textField.text = subjectPickerData[0]
} else {
...
}
}
}
Here is a short GIF outlining my issue. You can see me scrolling down the page, where I am not able to bounce horizontally, and then once I tag on the text field, all of the sudden the scroll view allows the bounces.
GIF
I'm pretty lost with this issue, so any help would be appreciated.
Edit
To clarify, I explicitly declare my scrollView's content size to be equal the desired height and the width of the screen that the user is on. I then set the bodyView's width to equal the same value. This is done in viewDidAppear with the following code:
// Fit the content to the screen
scrollView.contentSize = CGSize(width: UIScreen.main.bounds.width, height: 1200)
bodyView.frame.size.width = UIScreen.main.bounds.width
I also have constraints which force the scrollview and body view to both have the same width as the UIViewController's default child view (the parent of the scroll view in my hierarchy).
One interesting thing that I've noticed is that when I print the width of my scroll view and my body view when the views load, I receive the following output for iPhone 6:
385.0
385.0
This is correct as that is the width of an iPhone 6. However, when I tap on the text field, and then print the same values, I get this output:
385.0
384.0
So for some reason, my body view is one point smaller than my scroll view. I've tried setting the body view's width to be equal to the scroll view when I tap on the text field, like I do in the viewDidAppear function, but this had no effect.
In terms of the UIPickerView, I initialize a pickerview with my class instance variables like so:
var subjectPickerView = UIPickerView()
I then assign this picker view to be the input view for the text field in viewDidLoad:
textField.inputView = subjectPickerView
So I'm not sure if this makes the picker view a subview of the scroll view, but it's just replacing the keyboard in this scenario.
Thanks to #AchmadJP's comment, I tried explicitly creating an equal widths constraint between my scroll view and my body view. This seems to have solved the issue.
The reason I had not done this previously was that the body view's leading space, trailing space, top space and bottom space were constrained to be the same as those of the scroll view. Theoretically, this should have meant that the widths were equal at all times, but apparently, that is not the case.
For anyone else with the same problem, you can see this answer for the solution.
I have a requirement in which I need to have the following functionality -
1)I have a custom segmented control. In order to implement paging to the segmented control I have used horizontal scroll view. Each page has its own vertical scroll view.
Requirement
1)The image should hide as user scrolls up in the respective pages and should show down when user scrolls down in respective pages but keeping the custom segment always at the top of the screen when image is hidden irrespective of the individual page selection-
What I have tried so far -
1st Method
I tried putting the image as header of a table view.
Created a single section with one cell & gave the section header as the custom segment. And in the cell I placed the horizontal scroll view with the cell's height adjusted to cover all portion left out of the superview but it didn't work out as when I scroll the vertical scrolling of individual pages it was not in sync with the table view.
2nd Method
I tried setting the segment initially with a fixed distance from the top & I increased & decreased the constraint inside scrollViewDidScroll(). But it too didn't work as when the user scrolled rapidly ,the changing of constraint value didn't follow correctly.
So is there any other way to achieve the same ?
Please suggest as I can't make out what to do?
You add a tableView and your UIImage on top of it inside a scrollView. The tableView must have the same height & width than your scrollView. Then you disable the pan gesture of the scrollView :
self.scrollView.panGesture.active = false
Then you have to implement a custom scroll in scrollViewDidScroll' of yourtableView`'s delegate:
func scrollViewDidScroll(scrollView: UIScrollView) {
if self.scrollView.contentOffset.y <= 100 {
self.scrollView.contentOffset.y += scrollView.contentOffset.y
self.tableView.contentOffset.y = 0
} else {
// let the tableView scroll normally
}
}
Or, you can have a try with https://github.com/bryankeller/BLKFlexibleHeightBar ;)
It's a great component that can handle many type on animation in the header based on the position of a scrollView.