Rectangle 27 6

I did some playing around with the native Photos app, and I think I can say with confidence they are using a single UIScrollView. The giveaway is this: zoom in on an image, and pull to the left or right. You will see the next or previous photo. If you tug hard enough, it will even page to the next photo at 1.0f zoom. Flip back and the previously zoomed photo will be back to 1.0f zoom as well.

  • A single UIScrollView and a single UIScrollViewDelegate
  • Populate the UIScrollView with UIImageView children
scrollViewDidScroll:
  • Do some math and figure out what page you are currently on
viewForZoomingInScrollView:
  • Return a different view depending on the page index
  • Listen for scrollViewDidEndZooming:withView:atScale: and optionally do some anti-aliasing, etc based on the content

If you decide to try that out, let me know how it works out for you. I'd love to know how you finally end up getting this to work. Even better, post it to github.

ios - UIScrollView image/photo viewer with paging enabled and zooming ...

ios iphone uiscrollview
Rectangle 27 1

I did some playing around with the native Photos app, and I think I can say with confidence they are using a single UIScrollView. The giveaway is this: zoom in on an image, and pull to the left or right. You will see the next or previous photo. If you tug hard enough, it will even page to the next photo at 1.0f zoom. Flip back and the previously zoomed photo will be back to 1.0f zoom as well.

This is wrong. I'm using nested scrollviews, and getting exactly the same effect. If you're using some memory management scheme (which I had to start using... my page number is fairly high ('bout 50 each in 2 scrollViews)), then you can use a mechanism similar to whatever you have triggering your page loads / unloads to trigger a zoom reset for the pages -1 and +1 from the current page.

I suspect that apple sets this off as soon as the previous pic has disappeared.

What I don't understand is how to achieve smooth scrolling between pages - there's always a very short hang at the moment of transition. Do not get it. I've gotten pretty deep into fixing it - NSInvocationOperations were my first stop, then I made a reusable views queue for the page views (which retain their image views)... still this durned hang.

I only have one NSOperationQueue running, and I've tried fiddling with the max number of concurrent operations. My thought was that the main thread was getting clogged by competing Queues, or maybe even one queue trying to do to much... still, the hang.

I even tried creating super low-qual versions of my media, in case that was the problem. With each image weighing in at around 10k (these are jpegs, mind you)... you guessed it. The hang's still there.

I'm pretty much resolved to do what I've done before and use TTPhotoViewController from Three20. I've spent some hours swimming through that code, and it's always a great education. At this point, though, I would really like to know where the heck this hang comes from, if only so I can spend my can't-sleep hours wondering about something less brain boiling.

Have you tried a CATiledLayer?

Nope... have you?

ios - UIScrollView image/photo viewer with paging enabled and zooming ...

ios iphone uiscrollview
Rectangle 27 2

Solution I found (More of a workaround) declare a boolean called hidden. Then I overrode these methods:

func scrollViewDidScroll(scrollView: UIScrollView){
    let xOffset = scrollView.contentOffset.x;


    if(xOffset > scrollView.contentSize.width/4)
    {
        if hidden == true {
            print("\nShow status bar\n")

            hidden = false
            UIView.animateWithDuration(0.3, animations: {
                self.setNeedsStatusBarAppearanceUpdate()
            })
        }
    } else
    {
       print("\nHide Status Bar\n")

        hidden = true
        UIView.animateWithDuration(0.2, animations: {
            self.setNeedsStatusBarAppearanceUpdate()
        })
    }
}

override func preferredStatusBarUpdateAnimation() -> UIStatusBarAnimation {
    if hidden == false {
        return UIStatusBarAnimation.Fade
    } else {
        return UIStatusBarAnimation.Slide
    }
}

override func prefersStatusBarHidden() -> Bool {
    print("\nstatus Bar Changed to hidden = \(hidden)\n")
    return hidden
}

It fades the status bar in once you've at least scrolled half way, and slides the status bar back up once you've gone back half way again.

ios - UIScrollView dynamic status bar - Stack Overflow

ios swift uiscrollview uinavigationcontroller uistatusbar
Rectangle 27 6

I think this might be a bug in iOS autolayout wrt UIScrollViews. I was having a similar problem only in my case, I would push to a detail view, then pop back to the master view which is a UIScrollView. Content in the scroll view would be shifted up. Scrolling the scroll view to the top, then pushing and popping again and things would be properly reset.

I even tried plain out of the box master detail app using a UIScrollView instead of a table view as master, and was able to demonstrate the defect.

Table views and Collection views do not seem to have this problem.

iphone - iOS AutoLayout issue with ScrollView - Stack Overflow

iphone ios uiscrollview modalviewcontroller autolayout
Rectangle 27 7

In case it isn't solved yet, i solved the problem for me.

I added a UIPanGestureRecognizer to a UIScrollView to detect two finger pan gestures and the default UIScrollView behaviour (scrolling to something) still workes.

So what i did is to add the UIPanGestureReconizer to the UIScrollView:

UIPanGestureRecognizer *pangestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(displayReloadIndicator:)];
pangestureRecognizer.minimumNumberOfTouches = 2;
pangestureRecognizer.delegate = self;
[self.scrollView addGestureRecognizer:pangestureRecognizer];
[pangestureRecognizer release];

After this i added the code:

After this, i implemented the pan gesture recognizers action method.

- (void) displayReloadIndicator:(UIPanGestureRecognizer*) panGestureRecognizer {

    UIGestureRecognizerState gestureRecognizerState = gestureRecognizer.state;

    CGPoint translation = [gestureRecognizer translationInView:self.scv_bibgesamt];

    if (gestureRecognizerState == UIGestureRecognizerStateBegan) {

        // create a UIView with all the Pull Refresh Headers and add to UIScrollView
        // This is really much lines of code, but its simply creating a UIView (later you'll find a myRefreshHeaderView, which is my base view) and add UIElements e.g. UIActivityIndicatorView, a UILabel and a UIImageView on it
        // In iOS 6 you will also have the possibility to add a UIRefreshControl to your UIScrollView

    }

    else if (gestureRecognizerState == UIGestureRecognizerStateEnded
             || gestureRecognizerState == UIGestureRecognizerStateCancelled) {

        if (translation.y >= _myRefreshHeaderView.frame.size.height + 12) { // _myRefreshHeaderView is my baseview

         //so the UIScrollView has been dragged down with two fingers over a specific point and have been release now, so we can refresh the content on the UIScrollView
         [self refreshContent];

         //animatly display the refresh view as the top content of the UIScrollView
         [self.scrollView setContentOffset:CGPointMake(0, myRefreshHeaderView.frame.size.height) animated:YES];
    }

    else {

         //the UIScrollView has not been dragged over a specific point so don't do anything (just scroll back to origin)
         [self.scrollView setContentOffset:CGPointMake(0, 0) animated:YES];

         //remove the view (because it's no longer needed)
         [_myRefreshHeaderView removeFromSuperview];
    }
}

In case you may wish to integrate the swipe back functionality from your navigationcontroller, you should integrate following code:

- (void) viewDidLoad {

    [super viewDidLoad];


    if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) {

        self.navigationController.interactivePopGestureRecognizer.enabled = YES;

        self.navigationController.interactivePopGestureRecognizer.delegate = nil;
    }

    //setup view controller
}

and

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {

    if (gestureRecognizer == _panGestureRecognizer
        && [self.navigationController.viewControllers count] > 1) {

        CGPoint point = [touch locationInView:self.view.window];

        if (point.x < 20
            || point.x > self.view.window.frame.size.width - 20) {

            return NO;
        }
    }

    return YES;
}

ios - UIPanGestureRecognizer conflict with scrollview - Stack Overflow

ios uiscrollview uigesturerecognizer uipangesturerecognizer
Rectangle 27 419

I was also having a lot of issue with a UIScrollView composing of multiple UITextFields, of which, one or more of them would get obscured by the keyboard when they are being edited.

Here are some things to consider if your UIScrollView is not properly scrolling.

1) Ensure that your contentSize is greater than the UIScrollView frame size. The way to understand UIScrollViews is that the UIScrollView is like a viewing window on the content defined in the contentSize. So when in order for the UIScrollview to scroll anywhere, the contentSize must be greater than the UIScrollView. Else, there is no scrolling required as everything defined in the contentSize is already visible. BTW, default contentSize = CGSizeZero.

2) Now that you understand that the UIScrollView is really a window into your "content", the way to ensure that the keyboard is not obscuring your UIScrollView's viewing "window" would be to resize the UIScrollView so that when the keyboard is present, you have the UIScrollView window sized to just the original UIScrollView frame.size.height minus the height of the keyboard. This will ensure that your window is only that small viewable area.

3) Here's the catch: When I first implemented this I figured I would have to get the CGRect of the edited textfield and call UIScrollView's scrollRecToVisible method. I implemented the UITextFieldDelegate method textFieldDidBeginEditing with the call to the scrollRecToVisible method. This actually worked with a strange side effect that the scrolling would snap the UITextField into position. For the longest time I couldn't figure out what it was. Then I commented out the textFieldDidBeginEditing Delegate method and it all work!!(???). As it turned out, I believe the UIScrollView actually implicitly brings the currently edited UITextField into the viewable window implicitly. My implementation of the UITextFieldDelegate method and subsequent call to the scrollRecToVisible was redundant and was the cause of the strange side effect.

So here are the steps to properly scroll your UITextField in a UIScrollView into place when the keyboard appears.

// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.

- (void)viewDidLoad 
{
    [super viewDidLoad];

    // register for keyboard notifications
    [[NSNotificationCenter defaultCenter] addObserver:self 
                                             selector:@selector(keyboardWillShow:) 
                                                 name:UIKeyboardWillShowNotification 
                                               object:self.view.window];
    // register for keyboard notifications
    [[NSNotificationCenter defaultCenter] addObserver:self 
                                             selector:@selector(keyboardWillHide:) 
                                                 name:UIKeyboardWillHideNotification 
                                               object:self.view.window];
    keyboardIsShown = NO;
    //make contentSize bigger than your scrollSize (you will need to figure out for your own use case)
    CGSize scrollContentSize = CGSizeMake(320, 345);
    self.scrollView.contentSize = scrollContentSize;
}

- (void)keyboardWillHide:(NSNotification *)n
{
    NSDictionary* userInfo = [n userInfo];

    // get the size of the keyboard
    CGSize keyboardSize = [[userInfo objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;


    // resize the scrollview
    CGRect viewFrame = self.scrollView.frame;
    // I'm also subtracting a constant kTabBarHeight because my UIScrollView was offset by the UITabBar so really only the portion of the keyboard that is leftover pass the UITabBar is obscuring my UIScrollView.
    viewFrame.size.height += (keyboardSize.height - kTabBarHeight);

    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationBeginsFromCurrentState:YES];
    [self.scrollView setFrame:viewFrame];
    [UIView commitAnimations];

    keyboardIsShown = NO;
}

- (void)keyboardWillShow:(NSNotification *)n
{
    // This is an ivar I'm using to ensure that we do not do the frame size adjustment on the `UIScrollView` if the keyboard is already shown.  This can happen if the user, after fixing editing a `UITextField`, scrolls the resized `UIScrollView` to another `UITextField` and attempts to edit the next `UITextField`.  If we were to resize the `UIScrollView` again, it would be disastrous.  NOTE: The keyboard notification will fire even when the keyboard is already shown.
    if (keyboardIsShown) {
        return;
    }

    NSDictionary* userInfo = [n userInfo];

    // get the size of the keyboard
    CGSize keyboardSize = [[userInfo objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;

    // resize the noteView
    CGRect viewFrame = self.scrollView.frame;
    // I'm also subtracting a constant kTabBarHeight because my UIScrollView was offset by the UITabBar so really only the portion of the keyboard that is leftover pass the UITabBar is obscuring my UIScrollView.
    viewFrame.size.height -= (keyboardSize.height - kTabBarHeight);

    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationBeginsFromCurrentState:YES];
    [self.scrollView setFrame:viewFrame];
    [UIView commitAnimations];
    keyboardIsShown = YES;
}
viewDidUnload
contentSize
viewDidLoad
  • Shrink the UIScrollView when the keyboard is present
UIScrollView
  • Use an ivar to detect if the keyboard is already shown on the screen since the keyboard notifications are sent each time a UITextField is tabbed even if the keyboard is already present to avoid shrinking the UIScrollView when it's already shrunk

One thing to note is that the UIKeyboardWillShowNotification will fire even when the keyboard is already on the screen when you tab on another UITextField. I took care of this by using an ivar to avoid resizing the UIScrollView when the keyboard is already on the screen. Inadvertently resizing the UIScrollView when the keyboard is already there would be disastrous!

Hope this code saves some of you a lot of headache.

Great, but two problems: 1. UIKeyboardBoundsUserInfoKey is deprecated. 2. keyboardSize is in "screen coordinates", so your viewFrame calculations will fail if the frame is rotated or scaled.

CGSize keyboardSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
UIKeyboardBoundsUserInfoKey

HI, I did the same, but the text view only moves up when user starts typing? Is it the expected behavior or I am missing something?

[[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size
[[userInfo objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size

I like your solution but I think I can make it even simpler: don't bother with the Notification Observer stuff; instead call the right animation routines inside the appropriate delegate methods -- for UITextView they're textViewDidBeginEditing and textViewDidEndEditing.

ios - How to make a UITextField move up when keyboard is present? - St...

ios objective-c uitextfield uikeyboard
Rectangle 27 5

You must set the leading and trailing numbers to ZERO, never negative ...

scrollView with autolayout and without contentSize

  • Drag n drop a scrollView to viewController and apply whatever constraints to cover the space you want.
  • Drag n drop a UIView inside the scrollView and make it cover the whole space of scrollView and apply constraints to be top, left, right, bottom space from scrollView.
  • Set the height (and width if horizontal scrolling is required) of the inner view as per the need of scrolling. This part can also be done from code if required.
  • Critical. After you set the height to some large value in point (3), go back to point (2) and be certain to set the top, left, right, bottom values back to zero as Xcode may have changed them for you when you force changed the height in (3).

And you're done. Now, you can add any number of controls on this view and apply the constraints relevant to each other (which don't seem working without this view). If you don't want to use this view then you'll have to apply constraints for each control related to scrollView (not related to each other).

Critical. Let's say for clarity the UIScrollView is 1000 wide and 100 high. (In fact normally these values would be dynamic, of course, depending on the width of the device etc. But for now just say 1000 wide and 100 high.) Let's say you are doing a horizontal scroll. So put a UIView inside the UIScrollView. (That is the "content view".) Set all four constraints of the content view top, bottom, leading, trailing, to the scroll view. Make them all zero even if that seems wrong. Set the height of the content UIView to 100 and forget about that. Now: you want to scroll horizontally, so set the width of the content view to be let's say 1225.

Note that the width of the content view is now 225 bigger than the width of the parent scroll view. That's OK: in fact, you MUST do that. Note that

as you normally would

Interestingly, you can actually set the leading/trailing numbers to any positive value (try say "50") and it gives you kind of a margin of the bounce. (It often looks great: try it.) Any negative value on either end will "silently break".

Note that, infuriatingly, often Xcode (as of 7.3.1 anyway),

because it tries to automatically tally them for you. If so it will silently break. Set all four values to zero in the first instance. And set the width of the content view much wider than the "1000" in the example.

Edited: I've ended up with using UITableView instead of UIScrollView for most of my requirement. As tableView seems to me much more flexible and dynamic.

Good work, it helped me a lot.

ios - How can I use Autolayout to set constraints on my UIScrollview? ...

ios uiscrollview storyboard autolayout
Rectangle 27 5

The problem is that Paginenabled property of UIScrollView always scroll the scrollview to width of its bounds. So When you have 9 subviews it tries to scroll to 2nd page and shows you 2-9 subviews. In actual your page1 has subviews 1-8 and 2nd pgae has only 9 number subview. So The real problem is that UIScrollview's content width should be proportional to your number of Pages. In your case your 1 page holds 8 subviews. so content size should be Width of UIScrollview * no. of Pages.

// noOfpages = No of pages in scrollview (Each page has 8 subviews)
// noOfSubviews
int noOfpages = 
if(noOfSubviews%8==0){
   noOfPages = noOfSubviews/8;

}else{
   noOfPages = noOfSubviews/8 + 1;
}
scrollview.contentSize = CGSizeMake(scrollview.frame.size.width*noOfPages,scrollview.frame.size.height);

In this way scrolling on first page will take you to view 9. and scrolling back will take you back to subview 1-8.

ios - Why setting pagingEnabled to YES in UIScrollView automatically s...

ios uiscrollview uiscrollviewdelegate scroll-paging
Rectangle 27 6

_scrollView.pagingEnabled = YES;
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    if (scrollView == _scrollView || scrollView == _offersCollectionView) {

        CGFloat offersCollectionViewPosition = _offersCollectionView.contentOffset.y;
        CGFloat scrollViewBottomEdge = [scrollView contentOffset].y + CGRectGetHeight(scrollView.frame);

        if (scrollViewBottomEdge >= [_scrollView contentSize].height) {
            _scrollView.scrollEnabled = NO;
            _offersCollectionView.scrollEnabled = YES;
        } else if (offersCollectionViewPosition <= 0.0f && [_offersCollectionView isScrollEnabled]) {
            [_scrollView scrollRectToVisible:[_scrollView frame] animated:YES];
            _scrollView.scrollEnabled = YES;
            _offersCollectionView.scrollEnabled = NO;
        }
    }
}
_scrollView
_outerScrollView
  • _offersCollectionView is the _innerScrollView (which was UICollectionView #2 in my original post).
  • When I swipe up (so the view moves down), the offersCollectionView takes over the entire view, moving the other subViews out of the view.
  • If I swipe down (so the views up), the rest of the subviews come back into focus with the scrollView's bounce effect.

ios - Continuous vertical scrolling between UICollectionView nested in...

ios objective-c uiscrollview uicollectionview ios8
Rectangle 27 6

_scrollView.pagingEnabled = YES;
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    if (scrollView == _scrollView || scrollView == _offersCollectionView) {

        CGFloat offersCollectionViewPosition = _offersCollectionView.contentOffset.y;
        CGFloat scrollViewBottomEdge = [scrollView contentOffset].y + CGRectGetHeight(scrollView.frame);

        if (scrollViewBottomEdge >= [_scrollView contentSize].height) {
            _scrollView.scrollEnabled = NO;
            _offersCollectionView.scrollEnabled = YES;
        } else if (offersCollectionViewPosition <= 0.0f && [_offersCollectionView isScrollEnabled]) {
            [_scrollView scrollRectToVisible:[_scrollView frame] animated:YES];
            _scrollView.scrollEnabled = YES;
            _offersCollectionView.scrollEnabled = NO;
        }
    }
}
_scrollView
_outerScrollView
  • _offersCollectionView is the _innerScrollView (which was UICollectionView #2 in my original post).
  • When I swipe up (so the view moves down), the offersCollectionView takes over the entire view, moving the other subViews out of the view.
  • If I swipe down (so the views up), the rest of the subviews come back into focus with the scrollView's bounce effect.

ios - Continuous vertical scrolling between UICollectionView nested in...

ios objective-c uiscrollview uicollectionview ios8
Rectangle 27 5

The answer is a bit easier if you are only targeting iOS 5 and up, because in that case you really ought to reuse the UIScrollView panGestureRecognizer property.

In any case, the key step is to NOT reuse scrollEnabled, but instead to subclass UIScrollView, create your own property to manage this state, and override setContentOffset:.

- (void) setContentOffset:(CGPoint)contentOffset
    {
        if(self.programaticScrollEnabled)
            [super setContentOffset:contentOffset];
    }
  • Subclass UIScrollView (or subclass another subclass of UIScrollView, depending on your needs).
  • Declare the BOOL property and override setContentOffset: as described above.
  • In your setup code, set up a UIPanGestureRecognizer and set your state variable to allow programatic scrolling (assuming that's the default state you want): panRecognizer = [[[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handleGesture:)] autorelease]; //These properties may change according to your needs panRecognizer.cancelsTouchesInView = NO; panRecognizer.delaysTouchesBegan = NO; panRecognizer.delaysTouchesEnded = NO; [self addGestureRecognizer:panRecognizer]; panRecognizer.delegate = self; self.programaticScrollEnabled = YES;

Manage which gestures can occur simultaneously. In my case:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
    return YES;
}
  • Turn programatic scrolling back on wherever you need it. For example: - (void)handleGesture:(UIPanGestureRecognizer *)gestureRecognizer { self.programaticScrollEnabled = YES; } - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer { self.programaticScrollEnabled = YES; return YES; }

objective c - Continuous scrolling between UIPanGestureRecognizer and ...

objective-c ios uiscrollview uipangesturerecognizer
Rectangle 27 2

func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        if scrollView == self.collectionView {
            var currentCellOffset = self.collectionView.contentOffset
            currentCellOffset.x += self.collectionView.frame.width / 2
            if let indexPath = self.collectionView.indexPathForItem(at: currentCellOffset) {
              self.collectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true)
            }
        }
    }

It works but you see some weird visual effect every time you move from page to page.

ios - UICollectionView Horizontal Paging not centered - Stack Overflow

ios uiscrollview uicollectionview
Rectangle 27 2

func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        if scrollView == self.collectionView {
            var currentCellOffset = self.collectionView.contentOffset
            currentCellOffset.x += self.collectionView.frame.width / 2
            if let indexPath = self.collectionView.indexPathForItem(at: currentCellOffset) {
              self.collectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true)
            }
        }
    }

It works but you see some weird visual effect every time you move from page to page.

ios - UICollectionView Horizontal Paging not centered - Stack Overflow

ios uiscrollview uicollectionview
Rectangle 27 30

Setting non-weak delegates to nil is generally a good idea unless you know you don't have to. For UITableView and UIScrollView, I've experienced crashes on previous iOS versions with the following steps (it may help to run with zombies enabled):

  • Press Done or the back button or whatever to dismiss the VC.

This appears to happen because the scrolling animation is retaining a reference to the view, so the view outlives the VC. It crashes when sending the scroll event.

I've also seen crashes after dismissing a VC containing a UIWebView while a request is being loaded, where simply setting the delegate to nil was not sufficient (I think the workaround was to call [webView loadRequest:nil]).

Have you found this just in instances where you aren't using UITableViewController like the OP?

@BobSpryn I dunno; I haven't kept track (but generally I've used a custom VC because UITableViewController isn't quite flexible enough). It's entirely possible that UITableViewController unsets the table view's data source/delegate in -dealloc.

ios - Set delegates to nil under ARC? - Stack Overflow

ios delegates automatic-ref-counting
Rectangle 27 30

Setting non-weak delegates to nil is generally a good idea unless you know you don't have to. For UITableView and UIScrollView, I've experienced crashes on previous iOS versions with the following steps (it may help to run with zombies enabled):

  • Press Done or the back button or whatever to dismiss the VC.

This appears to happen because the scrolling animation is retaining a reference to the view, so the view outlives the VC. It crashes when sending the scroll event.

I've also seen crashes after dismissing a VC containing a UIWebView while a request is being loaded, where simply setting the delegate to nil was not sufficient (I think the workaround was to call [webView loadRequest:nil]).

Have you found this just in instances where you aren't using UITableViewController like the OP?

@BobSpryn I dunno; I haven't kept track (but generally I've used a custom VC because UITableViewController isn't quite flexible enough). It's entirely possible that UITableViewController unsets the table view's data source/delegate in -dealloc.

ios - Set delegates to nil under ARC? - Stack Overflow

ios delegates automatic-ref-counting
Rectangle 27 4

Is there any way to prevent the touch events to be bubbled up to the Notification Center scroll view? Using [self setExclusiveTouch:YES]; in the subclass did not solve it unfortunately.

No. Because of the remote view hosting that your Today widget is being presented inside, [self setExclusiveTouch:YES] doesn't quite do what you want.

The rough architecture in iOS 8.0 is:

Think of the touch as basically becoming cloned when it crosses the process boundary. Your view's exclusive touch desires are only relevant in your widget's process space/window, and don't propagate back outwards to the Notification Center which is hosting you remotely.

uiscrollview - Dragging Gestures in iOS 8 Today Extensions - Stack Ove...

ios uiscrollview ios8 notificationcenter today-extension
Rectangle 27 4

Is there any way to prevent the touch events to be bubbled up to the Notification Center scroll view? Using [self setExclusiveTouch:YES]; in the subclass did not solve it unfortunately.

No. Because of the remote view hosting that your Today widget is being presented inside, [self setExclusiveTouch:YES] doesn't quite do what you want.

The rough architecture in iOS 8.0 is:

Think of the touch as basically becoming cloned when it crosses the process boundary. Your view's exclusive touch desires are only relevant in your widget's process space/window, and don't propagate back outwards to the Notification Center which is hosting you remotely.

uiscrollview - Dragging Gestures in iOS 8 Today Extensions - Stack Ove...

ios uiscrollview ios8 notificationcenter today-extension
Rectangle 27 2

That's actually the very interesting topic. I'll try to explain the idea I came up with while working on another Calendar app.

In my case it was not a Calendar View but Day View, hours 00:00 - 24:00 listed from top to bottom, so I had UITableView and not UICollectionView but it's not that important in this case.

The language used was not Swift but Objective-C, so I just try to translate code samples.

Instead of manipulation with the data source I created UITableView with the fixed amount of rows, in my case to store exactly two days (48 rows, two days of 24 hours each). You could choose the amount of rows containing in two full screens.

The important thing is that total amount of rows must be a multiple of 8.

Then you need a formula to calculate what's the day number for each particular cell based on what's inside the first visible row.

The idea is that UICollectionView is in fact UIScrollView so when we scroll down or up we can handle the corresponding event and calculate the visible offsets.

To simulate the infinitive scrolling we handle the scrollViewDidScroll and check if you just passed the 1/8 of the UIScrollView height scrolling up, move your UIScrollView to the 1/2 of height plus the exact offset so it moves to the "second screen" smoothly. And back, if you passed the 6/8 of the UIScrollView height while scrolling down, move the UIScrollView up to 1/2 of its height minus offset.

viewDidLoad
calendarView.showsHorizontalScrollIndicator = false
calendarView.showsVerticalScrollIndicator = false
calendarView
UICollectionView

Here is the code (translated from Objective-C right here, not tried in the real project):

func scrollViewDidScroll(scrollView_:UIScrollView) {  
    if !enableScrollingHandling {return}
    var currentOffsetX:CGFloat = scrollView_.contentOffset.x  
    var currentOffSetY:CGFloat = scrollView_.contentOffset.y  
    var contentHeight:CGFloat = scrollView_.contentSize.height  
    var offset:CGFloat  

    /*  0.125 - 1/8,  0.5 - 1/2  */
    if currentOffSetY < (contentHeight * 0.125)  {
        enableScrollingHandling = false  
        offset = (contentHeight * 0.125) - currentOffSetY    

        // @todo: your code, specify which days are listed in the first row (2nd screen)

        scrollView_.contentOffset = CGPoint(x: currentOffsetX, y: currentOffset + 
        contentHeight * 0.5 + offset - CGFloat(kRowHeight))

        enableScrollingHandling = true
        return
    }  

    /*  0.75 - 6/8,  0.5 - 1/2  */
    if currentOffSetY > (contentHeight * 0.75)  {  
        enableScrollingHandling = false  
        offset = currentOffSetY - (contentHeight * 0.75)    

        // @todo: your code, specify which days are listed in the first row (1st screen)

        scrollView_.contentOffset = CGPoint(x: currentOffsetX, y: currentOffset - 
        contentHeight * 0.5 - offset + CGFloat(kRowHeight))
        enableScrollingHandling = true
        return
    }  
}

Then in your collectionView:cellForItemAtIndexPath: based on what's in the first row (is this content of the first screen or second screen) and what are the current visible days (formula) you just update the cell content.

This formula could also be a tricky part and may require some workaround especially if you decide to put Month Names in between months. I do also have some ideas on how to organise it, so if you encounter any problem we can discuss it here.

But such an approach to simulate infinitive scrolling with "two screens loop and jumps in between" works really like a charm, very smooth scrolling like behaviour tested on older phones also.

PS: kRowHeight is just a constant the height of the exact row (cell) it's needed for precise and smooth scrolling behaviour it could be skipped I think.

Important notice, I skipped this from original code, but see this is important. When you manually set the contentOffset you also triggers the scrollViewDidScroll event, to prevent this you need to temporary disable your scrollViewDidScroll event processing. You can do it by adding, for example, state variable enableScrollHandling and change its state true/false, see updated code.

wow, great answer, I'll prototype it right now, and then write what I get and about the formula, didn't know about such approach:) Thanks for such great post!

I wrote you in the chat, I made a prototype, you might be interested.

ios - UICollectionView dynamic data source items adding to the beginni...

ios swift uiscrollview uicollectionview uicollectionviewlayout
Rectangle 27 2

An alternative to this (that would avoid the need for casting) would be to set the UIViewController custom class back to UIView in interface builder. Then, insert a new UIScrollView as a subview (simply drag and drop in Interface Builder), connecting the UIScrollView to the UIViewController subclass via an IBOutlet that looks something like:

@property (nonatomic, weak) IBOutlet UIScrollView *scrollView;

(When you connect the outlet that property declaration should be generated for you by XCode).

Then, in your UIViewController subclass, you can do something like:

[[self scrollView] setContentSize:CGSizeMake(1024, 2000)];
[[self scrollView] setContentOffset:CGPointMake(0, 150)];

Thanks Will, I'm appreciate your help. But, doing such thing might break the application as thousand lines of code were made within this controller and several controller and custom class were wired with it. I can simply do search & replace but prefer not to touch any code for now. If there is not a better solution, I'll do this. Anyway, do you know what cause the problem, why do I have to cast it? Thanks again

view
UIViewController
UIView
[[self view] setContentSize:CGSizeMake(123, 123)];)

On second thoughts, I don't think that not using dot notation should make any difference. I think casting is your only sensible option if you want the UIViewController's top level view to be a UIScrollView.

Hey, thanks again will. Sorry for late reply, I was out for holiday. I tried not using dot notation, the result was same. Anyways, appreciate your help : )

Yeah, I'm quite sure I understand exactly what you did there, but casting sounds simpler (and therefore better) to me.

objective c - Default view (as UIScrollView) of UIViewController - Can...

objective-c ios cocoa-touch
Rectangle 27 1

1/ Since UICollectionViewDelegate conforms to UIScrollViewDelegate, you can get the starting offset of your scrollview with scrollViewWillBeginDragging: then in scrollViewDidScroll: you would compare the new offset's x value. If the new offset.x 's value is smaller than the starting one, set it to 0 and update your scrollview.

#pragma mark - UIScrollViewDelegate

- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
    offset = scrollView.contentOffset;
}

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {        

    CGPoint newOffset = scrollView.contentOffset;

    if (newOffset.x < offset.x) {
        // scrolling to the left, reset offset
        [scrollView setContentOffset:offset];
    }
}

Because there is inertia with scrolling in iOS, scrollViewDidScroll: is called a lot of time, so it may cause performance issues. You may reduce the number of call by targeting your offset with scrollViewWillEndDragging:withVelocity:targetContentOffset: from UIScrollViewDelegate.

2/ Or ou can just use the method scrollViewWillEndDragging:withVelocity:targetContentOffset: which I just spoke about, which sets the offset back to its beginning, with an animation.

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {

    CGPoint newOffset = scrollView.contentOffset;

    if (newOffset.x < offset.x) {
        // scrolling to the left, reset offset with animation
        targetContentOffset->x = offset.x;
    }
}

3/ You spoke of UISwipeGestureRecognizer, did you give a try to UIPanGestureRecognizer? This is what "simple hold and drag" is.

ios - UICollectionView that only scrolls right (no left, up or down) -...

ios uiscrollview uicollectionview