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:….
Related
So I want to create a Slider on the top of the UI, each item representing a Category (there will be five items). I don't want to switch to another Controller, when sliding to another Category, I just want to load new data into the current controller. So I guess the UIScrollView is the way to go.
See here for what I want to realize:
I have trouble now to show the name of the chosen Category in the middle of the Slider, and by the same time on the left and right its neighbors.
Using a effective AutoLayout is a also necessary.
Is putting Panels into the UIScrollView the right way?
I am new to iOS-Development and would appreciate any help.
Add a scrollview to the top of your controller, in code configure scrollcontentwidth to screenWidth*5 and on each swipe change reload the data of your controller.
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".
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.
Let's say I have a full-screen UIView that overlays the main screen when a button is touched, and then goes away when this overlayed view is touched. This UIView could either be added and removed from the current view using addSubview: and removeFromSuperview, or it could be added when the current view is initialized and then shown and hidden by accessing and setting the hidden property of the UIView. Which is generally faster and better for performance (or are they the same)?
I did try add imageView and try loop 1000000 times to hide and show in each loop and add remove in each loop. Result is hide and show take 1s to do 1000000 loop. And add remove take 3s. I do it in simulator :)
I'd bet show and hide will be faster. The other way requires object creation/destruction, and fiddling with subviews.
More importantly, I think show and hide will be simpler, and the fight against complexity is paramount.
As Clay says, showing hiding will probably be faster, but you would need sensitive instruments to detect the difference. It's going to be single-digit hundredths of a second at the most, and probably much less than that. You won't be able to sense the difference "by eye".
Thus what matters is other things, like what is the easiest to understand and maintain? One problem with making a view exist in the view controller and showing/hiding it at well is that the layout of the view covers the other contents of the view controller and makes it hard to manage.
You can create a second XIB (or an XIB that goes along with your storyboard) that has your view controller's class as it's "File's owner" and link up IBOutlets to the views you want. Then you load the view from an XIB when you need it, install it as a subview of your current view. Then you remove it from the superview when you're done with it. I use that approach a fair amount.
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.