Scrolling collectionview inside scrollview also scrolls scrollview - swift

I am making weather app. I can swap between different locations using collectionview with enabled paging. Inside every page there is view with another collectionview where it shows weather for different time. When i drag and release on child scrollview, so it moves like it has kinetic energy, on the edge when it stops scrolling, master scrollview starts to scroll like it takes leftover energy from child scrollview. I dont want it to be sort of connected, so it needs to act more like in Apple's weather app.
Video of problem
Also would appreciate any tips on my design
EDIT:
Actually, there is collectionview inside scrollview, not two collectionview's

One way I can think of is to detect what is being interacted with and then prevent scrolling capabilities based on that.
One such way to do that would be to override hitTest for the main collection view.
Here is what you can try:
1. Create a custom UICollectionView subclass
class CustomCollectionView: UICollectionView
{
override func hitTest(_ point: CGPoint,
with event: UIEvent?) -> UIView? {
// Get the view you are interacting with
let view = super.hitTest(point, with: event)
// Allow interaction if we are interacting with the main
// collection view itself
if view is CustomCollectionView {
return view
}
// Return nil to prevent interaction since we are
// interacting with something besides the main collection view
return nil
}
}
2. Then create your main / parent UICollectionView only using this custom class
private var containerCollectionView: CustomCollectionView!
So hopefully this will
Restrict the scrolling interaction when you interact with the parent collectionview directly, not through its subviews
Preserve the scroll within the child collection
Give it a go and see if this helps

Related

How can I invalidate flow layout with animation like in ios?

I'm making an app in Xcode for Mojave OSX.
I want to make a resize of a collectionview and items position of that.
Currently I call to invalidateLayout() method, but it recalculate and set sizes and positions without animations.
In the GIF you could see what is the current behavior, I need that the last item go to second row, in an animated way.
For this i try to override this methods of NSCollectionViewFlowLayout
open override func prepare(forAnimatedBoundsChange oldBounds: NSRect){
}
open override func finalizeAnimatedBoundsChange() {
}
But these never get called, and I don't know what code I need to animate this transition.
(Moved from question to an answer.)
You could override the viewDidLayout of your viewController (or assign an observer to the frame view) and execute this:
collectionView.animator().performBatchUpdates(nil)
This reloads your collectionviewLayout with an animation.

Horizontal NSCollectionView in vertical NSTableView does not select until collection view is clicked

I have a weird question regarding NSCollectionView. Basically, I have a collection view that scrolls horizontally in a vertically scrolling table view. I have implemented the data source and delegate of the collection view within my NSTableCellView subclass. The data source works just fine and the collection view is able to load some images.
Here comes the problem. I want the collection view to be selectable. I have implemented this delegate method into my table cell view subclass:
func collectionView(_ collectionView: NSCollectionView, didSelectItemsAt indexPaths: Set<IndexPath>) {
// Do things here
}
This method works, but with a caveat. It only gets called when I click on an empty space in the collection view first and then selecting a cell. If I only click on a cell, it will not get called, which is quite bizarre to me. Any idea on how to fix this?
I designed the table cell view in the storyboard, the collection view has it's Selectable and Allow Empty Selection checked in the storyboard. Allow Multiple Selection is not checked because I don't want multiple selection, but I tried having it on and it doesn't change anything.
Thanks
Subclass NSTableView. In your subclass, override validateProposedFirstResponder(_:for:) to return true for your collection view and its subviews (or maybe just return true always).

Pull To Refresh Stack View

I want to implement a pull to refresh feature on my current UIViewController. I have 10 buttons - 5 rows of vertical stack views in one horizontal stack view to create a grid. I would like to be able to pull to refresh here to run a function and update the button labels. What are my options?
You can't add a refresh control into a stack view for the purpose of refreshing the content.
The important thing here is you have to add the refresh control into a scroll view to let it be functional as expected:
UIRefreshControl
A standard control that can initiate the refreshing of a scroll view’s
contents.
UIRefreshControl
For this case I would encourage to let your grid to be as a UICollectionView instead of stack view.
In addition, What if you tried to add another button into the grid? the container view should be scrollable, which means that a collection view would more maintainable.
Furthermore, you might be able to get the leverage of the UICollectionViewController. Checking the following could be useful:
Is completely static UICollectionView possible?
How to create a static UICollectionView.
How to use pull to refresh in Swift?
Your best option is to go with a UICollectionView. Create a custom cell to you desired needs. After that you can add a refresh controller:
// Global variable
let pullToRefreshData = UIRefreshControl()
// Where you initialise the collectionView (eg. viewDidAppear())
collectionView.refreshControl = pullToRefreshData
pullToRefreshData.addTarget(self, action: #selector(refreshData), for: .valueChanged)
// This will be called when you pull from top of the collectionView
#objc func refreshData() {
// Maybe you need to call a server API, stop the reloading animation when you have the data
pullToRefreshData.endRefreshing()
// Refresh the data from the collectionView
collectionView.reloadData()
}
If you only need to use your implementation, maybe you can provide a context.
For this use case I'd suggest using UICollectionView, which has build in support for UIRefreshControl. But if your buttons are somewhat different or you want a bit easier and less robust solution, you could simply embed your UIStackView in UIScrollView and set it's refreshControl.
See: https://developer.apple.com/documentation/uikit/uiscrollview/2127691-refreshcontrol

Programmatically adding InterfaceBuilder-created views to an NSStackView squashes them to 0 height

I've been trying to add a custom view I've created in Interface Builder to an NSStackView, but am failing miserably.
This is my view controller. It currently does nothing but initialize the view from the NIB:
class ServiceControlViewController : NSViewController {
init() {
super.init(nibName: "ServiceControlView", bundle: nil)!
}
}
And here's how I'm trying to add the newly created view to the stack view (this happens in the app delegate):
#IBOutlet weak var stackView: NSStackView!
#IBAction func addButtonClicked(sender: AnyObject) {
let viewController = ServiceControlViewController()
stackView.addView(viewController.view, in: .top)
stackView.layoutSubtreeIfNeeded()
}
When I print the frame of the created view before it is added to the stack view it has the correct size. If I print it after, it is squashed to zero height. I suspect there is some auto-resizing magic going on that I don't understand, but I haven't been able to fix it.
Interestingly enough, if I set the ServiceControlViewController's view to e.g. be an NSButton, then it is correctly added and not squashed to zero height. It only happens with the Custom Views I create.
PS: My InterfaceBuilder-created view simply contains a bunch of buttons and a label:
PPS: I've added an MWE
It seems like there are no constraints on that custom view to make it larger than zero size.
You'll have to determine what its sizing behavior is, e.g. is it always the same size, or resizable with a minimum, etc.
And then in IB you can build the constraints necessary to create that effect. e.g.:
Alternatively, you could put these controls into a stack view to get a similar result.
Interestingly enough, if I set the ServiceControlViewController's view to e.g. be an NSButton, then it is correctly added and not squashed to zero height. It only happens with the Custom Views I create.
Correct. This is because an NSButton has something your custom NSViews do not have: an intrinsicContentSize. This is what the stack view uses to size and position your added views.
So, override intrinsicContentSize in your custom views and all will be well.

Custom views with UIPanrecognizer unintendetly locks UITableViewController

I would like to place custom views (created from nib) inside UITableViewCells. That works without issues, unless the custom view declares it's own single touch UIPanrecognizer. In those cases my custom view responds to the pan events, but that pretty much disables the ability to scroll the cells inside the table view. I tried registering the UIPanRecognizer with both the cell and the custom view, but that ability seems to be not allowed any more since IOS 9 (seems like this is also not how Apple wants touch events to be handled, I read somewhere that touch events should just be handled by one view)
I also failed to find a way to scroll the table view programmatically, which would also seem a little hacky.
What are my options when it comes to wanting to preserve the pan behaviour of the UITableView while still being able to respond to a pan event inside my custom view?
My use case is as follows: I want to reuse a custom view that can sideways-scroll that I created for another part of my app. My table view is set to only react to downward pans. Therefore the two pan actions are not interfering.
Two ways I have this solved currently are as follows:
The sideways scroll reacts to two-finger pans. This is super awkward and unintuitive.
All gestures on the custom view (except taps) get sent to its superview. A tap on the custom view disables the rerouting, which locks the table view and enables sideways scrolling by letting the custom view react to the sideways pan. Another tap restores the initial behavior. This is awkward since it asks the user to perform three separate actions for what should be just one.
I know that I can enable sideways panning for table views, but that doesn't solve my problem of wanting to reuse my custom view inside the cell.
Does anyone know a solution to this dilemma?
The solution was to create a custom UIGestureRecognizer.
I followed this tutorial, created a custom SidewaysPanGestureRecognizer, registered that instead and passed all gestures but SidewaysPanGestureRecognizer to the superview by implementing the UIGestureRecognizerDelegate method responsible for deciding if the view should handle the gesture:
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer,
shouldReceiveTouch touch: UITouch) -> Bool {
return gestureRecognizer is SidewaysPanGestureRecognizer
}