Completely prevent interaction with UICollectionView - ios

I have a UICollectionView that I want to show, but I may place a UIView overtop of it with a slightly translucent background. When that occurs I need to prevent the user from interacting with the collection view - they should only be able to interact with the view that appears overtop of it. To do that, I've made the view fill the collection view's bounds and that works well. However I noticed when users of VoiceOver use the app, if they tap on the view it will focus it but then if they swipe right to go to the next element it will focus the first cell in the collection view and allow interacting with it. How can I completely prevent interacting with the collection view for all users?
I've tried setting scrollingEnabled to false and also userInteractionEnabled to false for the collectionView but that didn't do the trick. The label I have within each cell is still accessible, therefore the entire collection view is accessible.

Looks like accessibilityElementsHidden is the property you want; should be able to set this to YES on the UICollectionView to hide that subtree. From docs:
You might use this property to hide views that are covered by the arrival of a new view. In this case, the hidden views might remain visible onscreen, but they are not the focus of the user’s actions.
...which sounds like a good match for your case.

If users really should be only able to interact with the view that is on top of it and no other view (including the collection view), consider setting accessibilityViewIsModal on the view that is on top.
To nicely see what accessibilityViewIsModal does in practice, consider seeing the excellent interactive Figure 1 at Adding accessible behavior by David Rönnqvist, in section "Implementing accessible modal views".

Related

"scrollsToTop" not working even though only one UIScrollView subclass has "scrollToTop" set in view hierarchy

There are a slew of topics on this site with similar questions, but no matter what I do I cannot get scrollsToTop working in my app:
set scrollsToTop=false in every scrollView subclass except one
set it to false in every one, add a new scrollView behind other views, give it a content offset and a large contentSize, no change (its delegate gets no message)
walk every subview from my 'view' looking for UIScrollView subclasses, none are set
My view structure is complex - childViewControllers, custom headers, etc. I just cannot find any way to get this feature to work.
Well, I have to give credit to a Q&A not on SO for the clue: most every other solution has suggested walking the view hierarchy from the current view down, but this author says to walk if from the window down.
When I did that, I found the culprit: the hidden master view in my UISplitViewController (the app's root view controller) has a table, and that table had scrollsToTop set to true.
I changed it to false, and voila - now I get a working scrollsToTop!

Computationally, which makes more sense when loading subviews into a view

From an expense point of view, which option provides the best computational results when adding subviews to a UIView's hierarchy:
Scenario 1
In viewWillAppear:animated or willLayoutSubviews remove all subviews of the container view using [[aView subviews] makeObjectPerformSelector:#selector(removeFromSuperView], reset all pointers or properties and then (re)initialise all subviews and add them to the container view hierarchy.
Scenario 2
Initialize and add all subviews in the viewDidLoad method (called once only, thus only adding them to the view hierarchy once, then in the viewWillAppear:animated or willLayoutSubviews methods setting the constraints or frames for each subview.
Scenario 2 is much more faster than the first one. Creating and deleting views is expensive you should try to limit as much as possible.
If you are using autolayout setting the constraints can also be done in the viewDidLoad, and setting the frame is not recommended.
So the best is to create and add all the subviews in the viewDidLoad set up the autolayout constraints there and do not set the views frame, only the constraints in other methods if needed.
Similar to your condition there is another condition :
Take the situation where you need to present a view when the user taps a button. There are at least two approaches to this:
1.Create the view when the screen is first loaded and hide it; then when you need it, show it.
2.Do nothing until you need to show the view. Then, create the view and show it, all at once.
Each approach has its own pros and cons.
Using the first method, you consume more memory because you
immediately create the view which holds on to that memory until it’s
released. However, when the user does taps the button, your app will
appear more responsive as it only needs to change the view’s
visibility.
Taking the second approach will have the opposite effect; by creating
the view only when it’s required, you consume less memory; however,
the app won’t appear as responsive when the button is tapped.

UIPageViewController: how to have "negative" spacing between view controllers (with scroll-transition style)

I have to implement a view controller (on iPhone, portrait only, full screen view) where the upper part of the view must have an horinzontal, paged scrolling behavior, potentially infinite.
I already used for similar purposes UIPageViewControllers, to take advantage of the datasource and delegate protocols, which are very helpul for manage memory and other stuff (keeping only 3 view controllers in memory, providing delegates to handle actions exactly when a transition is done and so on): so I think that in this case too this component is the best choice.
But here comes my problem. In the view I'm realizing, I have to let the user understand that he can swipe left and right to move to another view: a page control is not a good choice, since the scroll could be potentially infinite, so I would like to let a small portion of the views of the left and right view controllers to be visible.
Something like that:
link to the image (sorry I cannot include images in my posts yet)
Up to now I have not been able to figure out how to realize this. In the options during initialization, UIPageViewControllerOptionSpineLocationKey can be specified to set (from documentation) "Space between pages, in points": but this seems to work only with positive value, so that the space increases, while it ignores negative values that could reduce the space.
I hope there might be a solution using page view controllers, since at the same time I need to refresh a table view in the lower part of the screen when a transition is complete, and the delegate method of page controllers is ideal for this aim.
Maybe a collection view can be an alternate solution, but it is more complicated and I'm not sure how to obtain a behavior like the one I described to refresh the table view.
If needed I can attach some code and a screenshot of the prototype
Ok, I understand that this is not possible and why it is.
The datasource methods load, when needed, the view controllers that are before and after the current one. Making these view controllers' views always visible, as I desired, will require that the datasource loads more than one view controllers after (or before, depends on the direction of scrolling) the current one, to be ready for the pan actions (potentially, before the animation is ended by the user lifting up its finger, two view controllers "after" or "before" could become visible it my desired configuration), and this is not intended by UIPageViewController on iPhone, especially in portrait mode.
So actually, the only way to achieve that more than one view is visible in an horizontal-scrolling component at any time, is to implement a UIScrollview with horizontal paging and calculate the contentSize and other sizes accordingly.

Animating Child View Controller's views during transition

I have a Master custom container view controller, whose purpose is to swap between two different view controllers that each manage a form with text fields. We'll call these FormVC1 and FormVC2. Both of these controllers's views are managed by a XIB.
I can successfully swap between FormVC1 and FormVC2 using:
transitionFromViewController:toViewController:duration:options:animations:completion: while passing UIViewAnimationOptionTransitionCrossDissolve for the options argument.
Now I want to sweeten the transition a little, and when FormVC1 is being dismissed, I want the individual text fields to fly off to the left (one at a time), and when FormVC2 is being presented, I want the individual text fields to fly in from the right, one at a time.
I'm a little confused as to how to set this all up, and where exactly to place the specific text field animation code. Do I put that in the animation block within Master? Do I put it in each FormVC's appearance callbacks?
You have a few options, so pick the one that works best for you:
1) Make the container view controller contain a scrollview, with scrolling disabled, and scrollbars disabled. Set the two form vc's as child vc's of the container, and add the views into the scrollview next to each other, so only one is visible. At the appropriate time, show the next form by setting the content offset of the scrollview with animated set to YES.
2) Even better (more easily extendible to add arbitrary number of forms), use a UICollectionView instead of a basic scrollview, set it to use the horizontal linear layout, make a custom cell to hold your form vc class (disable scrolling and scrollbars), then to show the next form, scrollToItemAtIndexPath:: should do the trick.
3) Most basic, maybe good enough: Make your container a navigation controller and just push the form vc with the default transition.
All these solutions assume your form vc's are only the controls you want animated and the main view has a transparent background.
In the animations block of transitionFromViewController:toViewController:… change the frames of the elements that you want to be animated. To make it fly off to the left, subtract something like 200 from the frame.origin.x. To make them fly in from the right, start at a high x value (maybe 500) and animate it to the center.
To animate them sequentially, you could use different x values for each element. For something more complex, use animateWithDuration:delay:options:animations:completion: and set up the next animation in the former's completion block. You'd use that instead of transitionFromViewController:toViewController:….

Automatically scroll to a subview in a scrollview

I have implemented a scrollview that has dynamic content: multiple sub-views displaying photos of players with player names under. The flow is:
1. On the main window, a player is selected.
2. User launches another view to add a new player.
3. When the user goes back to the main menu, the scroll view is refreshed completely so that the new player photo and name is also displayed.
At this time, the scroll-view should still have the previously selected player in focus but instead it is showing it from the beginning.
So the question is: is there a method to automatically scroll to a specific position in the scrollview so the screen still has the selected player in focus?
Yes, there are two:
(1) scrollRectToVisible:animated:
(2) setContentOffset:animated:
You can read about both methods at UIScrollView Class Reference. Either way, you can use an instance variable to store the content offset or the visible rect when the user scrolls, and then use one of the methods I listed above to restore the scroll view position when the player data is updated.
Depending on what you are doing, you may find UITableView easier to implement because it is very customizable "out-of-the-box" and it has plenty of methods to help manage a list (including the ones I mentioned above, and more). If you're interested, have a look at UITableView Class Reference.
Simple, create a CGPoint and a BOOL that you can retain after the view change and set them when you initially navigate away from that page. Then in viewDidAppear you can check if the BOOL is YES and use:
[myScrollView setContentOffset:myCGPoint animated:YES];
Or, if you want the scroll view to appear already scrolled to the correct position, do the same thing except in viewWillAppear with the animated flag set to NO.

Resources