I have a collection view inside of UIViewController. The view controller also has another UIView (let's call it welcomeView) which is placed above the collection view. When I scroll through the collection view, I would like the welcomeView to scroll with it. I have looked at this answer https://stackoverflow.com/a/43215801/5124961 which pretty much sums up how id like it to look, minus the view sticking to the top. The only problem with that solution is that my collectionView already has a header, so adding the welcomeView as a sublayer to collection view adds it under the already existing header which I dont want.
by just using this snippet of code:
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let offset = collectionView.contentOffset.y
if(offset > 0){
self.welcomeView.frame = CGRect(x: 0, y: -offset, width: self.view.bounds.size.width, height: 100)
}else{
self.welcomeView.frame = CGRect(x: 0, y: 0, width: self.view.bounds.size.width, height: 100)
}
}
I get this effect:
I think the frame of the collection view has to change depending on the offset but not too sure on how to achieve this after playing around with it. I have also looked at a few other questions similar to mine, however, these were in objective C which I am not too familiar with. Does anyone have a solution? thank you.
If I guess correctly from your capture, your welcome view doesn't overlay the collection view.
Based on this guess, the solution would be:
make the collection view go to the top of your controller's view
your welcome view can overlap your collection view
to avoid that the welcome view overlaps the content of your collection view, modify the contentInset property of your collection view (the contentInset's top should be equal to the welcome view's height).
keep your snippet
the scroll indicator will probably disappear under the welcome view. A simple solution is to have a transparent background to the welcome view. Thanks to the contentInset and the delegate call, you're guaranteed that there's no overlap between the content of the collection view and the welcome view.
Related
Sorry in advance for the probably silly question - I'm a beginner
I'm generating a new UIView in viewDidLoad, providing some constraints to anchor it over the main view. When it comes to understand the size of the new view, I always get zero.
Here is a simplified version of my code which is not working:
override func viewDidLoad() {
super.viewDidLoad()
let myView = UIView(frame: .zero)
myView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(myView)
myView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
myView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -200).isActive = true
myView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
myView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
print(myView.bounds.width)
}
The width (but also height) is returning 0.
This happens both with .bounds and .frame.
Do you have any hints?
Thanks!
First question I will ask, does the UIView display on the screen?
Second, from the knowledge of view controller life cycle, the views are laid out in viewDidLayout Subviews.
So, you want to called myView.bounds.width in by overriding the viewDidLayout Subviews or viewDidAppear, when the views have finally appeared in the screens
Thanks for making the question more clear.
I will suggest you either reloadData on the second collection view on viewDidLayout subviews. That way, the two gets layout as it is now, then the second collection view gets layout for the second time after the first collection view is laid.
Another approach will be for you to use relative constraints. I.e. Making weight and height constraints of the items in the second collection view relative to the items in the first collection view. This way, everything else is done automatically for you at runtime. Even if the size of the items in first collection view change later which might affect items of the second collection view, it will be taken care off. (For accurate result if you can get it to work)
I will appreciate if you can share more of the code or screen shots of these two collections views. I could provide a better approach to creating them
I am create custom UIView using this code:
lazy var temporary: UIView = {
let view = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
view.backgroundColor = .white
return view
}()
Exist way use NotificationCenter notify when this view will be presented and removed from VC if I am use:
self.view.addSubview(temporary)
or
temporary.removeFromSuperview()
I'm not totally sure what you are asking. I think you're asking for a way to tell when your custom view is added as a subview of another view.
The easiest way to do that is to make your view a custom subclass of UIView, and implement didMoveToSuperview() or willMove(toSuperview:). Those methods get called when your view is added as a child view of another view.
If you really want to use the notification center you could have your custom view class broadcast a notification when it gets added to a superview.
I'm facing an annoying problem with UIScrollView that my buttons cannot be touched if they are outside of the scroll view but I dont know how to fix it now
I have tried some ways but no helps so far
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
var contentRect = CGRect.zero
for view in scrollContentView.subviews {
contentRect = contentRect.union(view.frame)
}
for view in scrollContentView.subviews {
contentRect = contentRect.union(view.frame)
}
scrollView.contentSize.height = contentRect.size.height
}
The code above just helps to make the scroll view scrollable
I also attached my sample project in this link
https://drive.google.com/open?id=19U8jecDNQbAnTFbG36KMRxHfaLLcaLDq
I strongly appreciate your advices. Thank you
You have not described your view hierarchy correctly. What you actually have is this:
Scroll view
Content view
Stack view
Buttons
The content view is what is causing the problem. Its height is pinned to the height of the view controller's main view — which is the height of the screen. But of course the stack view with its buttons is taller than the screen, in order to give you something to scroll to. So the lower part of the stack view, and the buttons at the bottom of the stack view, are below the bottom of the content view. Thus they are outside their superview. Thus they are untouchable. A view outside its superview (or its superview, or its superview, all the way up the view hierarchy) is untouchable.
I have got an UIButton on a storyboard ViewController. When I load data into the form and the layout is significantly changing the button does not recognise the touch action.
I have figured out that when button is visible on the scrollview right after it if filled with data, the touch action works.
If the data too long and the button is not visible at first, just when it is scrolled into the display, the touch action does not work.
I was checking if something is above the button, but nothing. I have tried to change the zPosition of the button, not solved the problem.
What can be the issue?
I have made custom classes from the UIScrollView and the UIButton to check how the touches event triggered. It is showing the same behaviour, which is obvious. If the button is visible right at the beginning, the UIButton's touchesBegan event is triggered. If the button moves down and not visible at the beginning, it is never triggered, but the scrollview's touchesBegan is called instead.
Depending on the size of the data I load into the page sometimes the button is visible at the beginning, but the form can be still scrolled a bit. In this case the button still work, so it seems that this behaviour is not depending on if the scrollview is scrolled before or not, just on the initial visibility of the button.
Is there any layout or display refresh function which should be called to set back the behaviour to the button?
The code portion which ensures that the contentview is resized for the scroll if the filled data requires bigger space.
func fillFormWithData() {
dispDescription.text = jSonData[0]["advdescription"]
dispLongDescription.text = jSonData[0]["advlongdesc"]
priceandcurrency.text = jSonData[0]["advprice"]! + " " + jSonData[0]["advpricecur"]!
validitydate.text = jSonData[0]["advdate"]!
contentview.layoutIfNeeded()
let contentRect = CGRect(x: 0, y: 0, width: scrollview.frame.width, height: uzenetbutton.frame.origin.y+uzenetbutton.frame.height+50)
contentview.frame.size.height = contentRect.size.height
scrollview.contentSize = contentview.bounds.size
}
Ok, so another update. I have coloured the contentview background to blue and the scrollview background to white. When I load the data and resize the layout constraints, the contentview is resizing as expected, however now the scrollview is going to the bottom. After I scroll the view it is resizing to the original size which fits the screen. Now the button is only recognised when I touch the are which is blue behind. With the white background it is not recognised anymore, so it seems that the scrollview is hiding the button.
Let me get this clear the button is added in storyboard and it is a spritekit project?? If you are using zPosition?? Why don’t u connect the UIButton via the assistant editor as an IBAction then the action is always tied to the button.
You can also do it differently
Create an SKLabelNode and put it on the screen where you want to have the button and then set a name to it as myButton
override func touchesBegan(_ touches: Set<UITouch>, with event:
UIEvent?) {
if let touch = touches.first {
let location = touch.location(in: self)
let tappedNodes = nodes(at: location)
for node in tappedNodes {
if node.name == "myButton" {
// call your action here
}
}
}
}
EDIT 1:
You could also try auto resizing your scrollView.content this works also if you are adding any views via the app or programmatically
private func resizeScrollView(){
print("RESIZING THE SCROLLVIEW from \(scrollView.contentSize)")
for view in scrollView.subviews {
contentRect = contentRect.union(view.frame)
}
scrollView.contentSize = CGSize(width: contentRect.size.width, height: contentRect.size.height + 150)
print("THE CONTENT SIZE AFTER RESIZING IS: \(scrollView.contentSize)")
}
EDIT 2: I think I found the issue with your project. You need to move the MessageButton(UzenetButton) above DispDescription label in the object inspector in that way it will always be above your message textView.
At the moment the UzeneButton is at the very far back in your view hierarchy so if your textView is resizing whilst editing it covers the button that is why you cannot click on it.
See #Endre Olah,
To make situation more clear do one more thing, set clipToBound property of contentview to true.
you will notice that after loading of data your button not fully visible, it means it is shifting out of bound of its parentView (ContentView)
And that's why button is not taking your touch. However, if you carefully touch upper part of button it still do its job. Because upper part is still in bound of ContentView
Solution :
After loading of data you have to make sure that you increase height of ContentView such that button should never go out of bound of its parentView(ContentView).
FOR EXAMPLE
#IBOutlet var heightConstraintOfContentView : NSLayoutConstraint!
After loading of data
let contentRect = CGRect(x: 0, y: 0, width: scrollview.frame.width, height: uzenetbutton.frame.origin.y+uzenetbutton.frame.height+50)
heightConstraintOfContentView.constant = contentRect.size.height
contentView.layoutIfNeeded()
I use following steps when I need to use scrollview with dynamic content:
1) Firstly add a scrollView with top, bottom, trailing and leading is 0 to super view.
2) Add a view to scrollView and view's trailing, leading bottom and top space to scrollView can be set to 0 (or you can add margin optionally).
3) Now, you should add UI elements like buttons, labels with appropriate top, bottom, trailing and leading margins to each other.
4) Lastly, add equal height and equal width constraint to view with Safe Area:
and change equal height priority of view to 250:
It should solve your problem with UIScrollView.
Finally, I have found the solution in another chain, once it became clear that the scrollview's contentview is resizing on scroll event to the original size. (Not clear why this is like this, but that is the fact.)
So I had to add a height constraint to the contentview in the storyboard and create an outlet to it and adjust this constraint when the content size is changing, like this:
#IBOutlet weak var ContentViewHeight: NSLayoutConstraint!
func fillFormWithData() {
dispDescription.text = jSonData[0]["advdescription"]
dispLongDescription.text = jSonData[0]["advlongdesc"]
priceandcurrency.text = jSonData[0]["advprice"]! + " " + jSonData[0]["advpricecur"]!
validitydate.text = jSonData[0]["advdate"]!
contentview.layoutIfNeeded()
let contentRect = CGRect(x: 0, y: 0, width: scrollview.frame.width, height: uzenetbutton.frame.origin.y+uzenetbutton.frame.height+50)
contentview.bounds = contentRect
scrollview.contentSize = contentRect.size
----------- This is the key line to the success ----------
ContentViewHeight.constant = contentRect.size.height
----------------------------------------------------------
}
After this is added, it works perfectly.
I am trying to reproduce natively the TVML template that provides a grid of clickable images that extends beyond the screen's bounds. I am using a scroll view for this attempt, but I am unable to select elements that are added to the scroll view, but outside its visible area.
The sketch code using buttons for simplicity is as follows:
let dim = 50
for i in 0..<10 {
for j in 0..<10 {
let frame = CGRect(x: i * (dim + 10), y: j * (dim + 10), width: dim, height: dim)
let button = UIButton(type: .System)
button.frame = frame
myScrollView.panGestureRecognizer.allowedTouchTypes = [UITouchType.Indirect.rawValue]
myScrollView.addSubview(button)
}
}
The scroll view is sized such that only half of these buttons are visible. Why is the scroll view not scrolling to the buttons outside this area (using Siri remote)?
I thought the panGesture touchType might help, but it didn't.
Am I missing something obvious?
Set contentSize property to your scrollview. Make sure all components comes under given content size.
myScrollView.contentSize = CGSizeMake(1880, 2000)
It would actually be way easier to just use a UICollectionView. If you add an image to each cell, you'll get exactly the behavior you want after adjusting the collection view to what you want.
This tutorial kind of explains how it works. http://www.brianjcoleman.com/tutorial-collection-views-using-flow-layout/