I have an NSCollectionView and have to disable its scrollview scrolling property so that it doesn't get reloaded on some specific condition and show user an alert if he tries to scroll.
Any delegate method which is called when a view is scrolled which can restrict scrolling and reloading of collection view data would help.
Thanks in advance.
Subclass NSCollectionView and override scrollWheel
import Cocoa
class YourCollectionView: NSCollectionView {
var disableScroll = true
override func scrollWheel(with event: NSEvent) {
if disableScroll {
return
} else {
super.scrollWheel(with: event)
}
}
}
You can do with the help of scrollViewDidScroll method of ScrollViewDelegate.You can use this method because collectionview is subclass of scrollview.
For Example :
func scrollViewDidScroll(_ scrollView: UIScrollView) {
print("collectionView Content : \(collectionView.contentOffset.y)")
if collectionView.contentOffset.y >= 100
{
collectionView.isScrollEnabled = false
}
else
{
collectionView.isScrollEnabled = true
}
}
I hope its work for you.
Related
I created a vertically scrolling NSTableView. Each cell of this table view contains a title and beneath is a horizontally scrolling NSCollectionView. When the cursor is on the title when scrolling, the vertical tableview scrolls fine, but when the cursor is on the horizontally scrolling collection view the table view doesn't scroll.
How can I get the table view to scroll even when the cursor is on the collection view? I understand that the tableview and scrollview use two separate scroll views, but I don't know if there is a way to synchronize these two.
Ok thank god, I found a solution here on stackoverflow after all. It was in Objective-C so I translated it to Swift. Here it is for anybody looking at a similar problem:
class MTHorizontalScrollView: NSScrollView {
var currentScrollIsHorizontal = false
override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
}
override func scrollWheel(with event: NSEvent) {
if event.phase == NSEvent.Phase.began || (event.phase == NSEvent.Phase.ended && event.momentumPhase == NSEvent.Phase.ended) {
currentScrollIsHorizontal = fabs(event.scrollingDeltaX) > fabs(event.scrollingDeltaY)
}
if currentScrollIsHorizontal {
super.scrollWheel(with: event)
} else {
self.nextResponder?.scrollWheel(with: event)
}
}
}
Full credit to SO LINK
I am trying to make view that is scrollable. That viewcontroller contains also navbar. Now my goal is to resize my view if the title in that view touches the navbar. How should I do it?
This is how my view looks like(note that the navBar is just transparent):
What I want after the title collision:
I know that I can achieve it in scrollViewDidScroll delegate function, but how?
Well you can track label position using convertRect: method as
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let labelTop = label.rectCorrespondingToWindow.minY
let navBottom = self.navigationController?.navigationBar.rectCorrespondingToWindow.maxY
if navBottom == labelTop {
// do what you want to do
}
}
extension UIView{
var rectCorrespondingToWindow:CGRect{
return self.convert(self.bounds, to: nil)
}
}
I want to have my tableViewHeaders visible as the user scrolls by pinning to the top which is the current behaviour in my tableView. However, when the tableView stops scrolling, I want to remove these 'pinned' headers. I am achieving this in my collectionView project using the following in my scrollView delegate methods:
if let cvl = chatCollectionView.collectionViewLayout as? UICollectionViewFlowLayout {
cvl.sectionHeadersPinToVisibleBounds = false
cvl.invalidateLayout()
}
Is there a similar way to hide a tableView's 'pinned' (sticky) headers? I am using a tableViewController.
This is my solution to this issue. I wonder if there is a simpler way to do this though.
Please note, this will only work if your header is a UITableViewHeaderFooterView. Not if you are using a UITableViewCell for a header. If you are using a UITableViewCell, tableView.headerView(forSection: indexPathForVisibleRow.section) will return nil.
In order to hide the pinned headers when the tableView stops scrolling and have them re-appear when the tableView starts scrolling again, override these four scrollView delegate methods.
In the first two (scrollViewWillBeginDragging and scrollViewWillBeginDecelerating), get the section header for the first section of the visible rows and make sure it is not hidden.
In the second two delegate methods, do a check to see that for each of the visible rows, the header frame for that row is not overlapping the frame for the row cell. If it is, then this is a pinned header and we hide it after a delay. We need to ensure that the scrollView is not still dragging before removing the pinned header as will be the case when the user lifts their finger but the scroll view continues to scroll. Also because of the time delay, we check that the scrollView is not dragging before removing it in case the user starts scrolling again less than 0.5 seconds after the scroll stops.
override func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
showPinnedHeaders()
}
override func scrollViewWillBeginDecelerating(_ scrollView: UIScrollView) {
showPinnedHeaders()
}
override func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
removePinnedHeaders()
}
override func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
removePinnedHeaders()
}
private func showPinnedHeaders() {
for section in 0..<totalNumberOfSectionsInYourTableView {
tableView.headerView(forSection: section)?.isHidden = false
}
}
private func removePinnedHeaders() {
if let indexPathsForVisibleRows = tableView.indexPathsForVisibleRows {
if indexPathsForVisibleRows.count > 0 {
for indexPathForVisibleRow in indexPathsForVisibleRows {
if let header = tableView.headerView(forSection: indexPathForVisibleRow.section) {
if let cell = tableView.cellForRow(at: indexPathForVisibleRow) {
if header.frame.intersects(cell.frame) {
let seconds = 0.5
let delay = seconds * Double(NSEC_PER_SEC)
let dispatchTime = DispatchTime.now() + Double(Int64(delay)) / Double(NSEC_PER_SEC)
DispatchQueue.main.asyncAfter(deadline: dispatchTime, execute: {
if !self.tableView.isDragging && header.frame.intersects(cell.frame) {
header.isHidden = true
}
})
}
}
}
}
}
}
}
Additionally add removePinnedHeaders() to viewDidAppear() and any other rotation or keyboard frame change methods that will scroll your tableView.
I have a WKWebView and I want to programatically enable/disable pinch to zoom.
What should I return when I want to enable pinch to zoom?
wkWebView!.scrollView breaks with
'The view returned from viewForZoomingInScrollView: must be a subview
of the scroll view. It can not be the scroll view itself.'
func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? {
if pinchToZoom {
return ???
} else {
return nil
}
}
if (pinchToZoom)
{
for (UIView *subScrollView in [scrollView subviews])
{
if ([subScrollView isKindOfClass:NSClassFromString(#"WKContentView")])
{
return subScrollView;
}
}
return nil;
}
else
{
return nil;
}
My solution was to set self as webView.scrollView's delegate only when disabling zoom, and reset it to nil if zoom is enabled.
var pinchToZoom: Bool {
didSet {
// only set self as delegate when disabling zoom
webView.scrollView.delegate = pinchToZoom ? nil : self
}
}
func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? {
return nil
}
This way, as soon as pinchToZoom is set, the delegate updates to enable/disable zooming. No private API needed. Hope this helps!
When trying to scroll a view by touching and dragging on a text field, similarly to how you would scroll around on the edit contacts view on the iPhone, the view will not move, touching and dragging in between the text fields or off to the side of the text fields in the scroll view works but not when touching a textfield.
Any insights would be greatly appreciated. Thanks!
Try setting the delaysContentTouches property to YES for the UIScrollView to which you have added to text field. This should allow you to scroll if you touch and then drag within a text field.
Setting delaysContentTouches to true didn't work for me either. What did was iterating through the scrollview's gesture recognizers and setting each of their delaysTouchesBegan and cancelsTouchesInView to true and false, respectively:
scrollView.gestureRecognizers?.forEach {
$0.delaysTouchesBegan = true; $0.cancelsTouchesInView = false
}
I think it solution in this situation
extension UITableView {
override public func touchesShouldCancelInContentView(view: UIView) -> Bool {
if view is UITextField || view is UITextView {
return true
}
return super.touchesShouldCancelInContentView(view)
}
}
try with below approach. Just replace your UIScrollView with ScrollView and adds the code to your project.
SWIFT 4.1
class ScrollView: UIScrollView {
override func touchesShouldCancel(in view: UIView) -> Bool {
if type(of: view) == TextField.self || type(of: view) == UITextView.self {
return true
}
return super.touchesShouldCancel(in: view)
}
}