UIScrollView with pagination + showing part of the previous/following pages - ios

I'm trying to create a kind of a "game mode" menu similar to the one used by the "Cut the Rope" game to select the level pack:
What I want in particular is to achieve the same effect of showing the "current item" (in this case, the "2. Fabric Box" item) plus a bit of the previous and following items (to make sure the user is aware that there are more modes available by scrolling), with pagination enabled (to make the scroll view automatically "center" on these items).
This seems like a natural job for a UIScrollView with pagination enabled, however from the documentation it seems the pagination occurs on multiples of the view bounds.
So: if pagination occurs on multiples of the view bounds, is there any way to achieve this effect with a UIScrollView?
The fact that we see the full width of the screen would suggest that the UIScrollView frame's width would be 320px in this case, but each individual item would need to be smaller than that in order to show that little bit of the previous and next items, thus messing up the pagination...

For your reference, you can see a sample implementation of a page control from here.
https://developer.apple.com/library/content/samplecode/PageControl/Introduction/Intro.html
For the implementation you want,
to your surprise, the scrollview's width is actually smaller than 320 (or 480). The magic property to set is:
scrollView.clipsToBounds = NO
The only problem with this implementation is that the scrollview get no touch events if the touch is outside the bounds of the scrollView. This can be fix by passing its parent hitTest event to scrollView.
Just to link to to a better explanation:
UIScrollView horizontal paging like Mobile Safari tabs
Slightly different from what I recommend but does the same thing.
Edit:
I have a small project called LXPagingViews that does the above, hopefully in an out of the box manner (Do drop me a pull request or feedback in issue): https://github.com/lxcid/LXPagingViews

Related

XCUITests: Can't tap element on scrollview while views are stack on each other

Sample Project
This is a sample project that showing the issue. It's storyboard based, but method of building interface doesn't matter. It's UIViewController with UIScrollView for entire screen and 128 pts height view that is on top of this UIScrollView.
Inside scroll view there is an UIView that has 2000 pts height and UIButton in the center.
Initial State
After light scroll
At the bottom of UIScrollView
Link here: https://github.com/JakubMazur/UITestsDemo
Problem
I'm trying to tap this green button with XCUITest using app.buttons["Tap Me!"].tap()
XCUITest get identifiers from elements on screen for entire scroll view that works fine.
According to this reply on a thread on Apple Developer Forum written by Apple Framework Engineer I shouldn't scroll manually to get to the button and yes, this is partially true.
What is happening when code from (1) is executed is that button is scrolled just enough to be visible on screen but it's still not hittable, because other (purple view) is on top of UIScrollView
What is working
If I run a test written like this:
func testThatDoWorkButItsSlow() {
app.scrollViews.firstMatch.swipeUp()
app.buttons[buttonLabel].tap()
}
that is scrolling up and then looks for a button this will work, but it's slow and so inaccurate that is hardly usable.
What I cannot do
Disabling userInteractions on purple view. In real example I still need touches for this (purple) view.
Questions
Is there a way to use precise scrolling in XCTest for this case?
Or is there a way to set contentOffset scrollview to other value that will make this button more centered on a screen compared to action of tap()?
Or there is a way to fast scroll to the bottom (without animations) and maybe moving only up for each element?
My recommendation here would be to use the XCUICoordinate.press(forDuration:thenDragTo:) method to scroll.
https://developer.apple.com/documentation/xctest/xcuicoordinate/1615003-press
You can create a XCUICoordinate for the yellow view, then drag it slightly upwards to expose the button and make it hittable.
In most cases, the automatic scroll should work, but it seems like in this case a manual scroll/drag is necessary.
The UI Testing should replicate human interactions. You cannot expect from a human being to scroll "153px", you can just expect to "scroll until".
You can try something like :
while (!app.buttons["Tap Me!"].isHittable) {
app.swipeUp()
}
NB: You may also want to add a condition to leave the while loop if you can't find the button after a reasonable amount of attempts

iOS: How to display a box of 'tappable tags'

My use-case is like this:
The user defined some tags (text like "#asdf", "#qwerty", "#let_me_think_about_it" and "#decide later"). I want to display these in a box without scrolling (and don't know, how many tags the user created until I display the box).
The box itself should not be scrollable at all but be shown in a UITableViewCell (which is being scrolled). So it must compute the proposed height and respond to Autolayout mechanisms. If a (ARM) Mac user resizes the window to be smaller than before (or an iOS user rotates the device), the box should increase/decrease its height, as necessary (within the limits of Autolayout, since I know of some issues). Each of the tags should be (de)selectable at the same time (UILabel with UITapGestureRegognizer attachted?) and be able to displayed 'selected' (via a background view).
So, the box should primary try to align all content horizontal. If There's not enough horizontal space, do a "line break" and continue on the next "line".
My current solution is a UIScrollView that the user can scroll horizontal and tap any of the (UILabel) views. The displayed views itself are being loaded from a NIB file, like a UITableView does. The issue here is that not any of the selected tags might be visible at the first glance.
If there was no Autolayout, I'd exactly know what to do. But since there it is, I want to use Autolayout in my NIB files and wonder what you would do?
(How do you compute the required width of such a view and decide when a line break is to be done (and how?))
I think I need a simple hint. But if it needs code to explain, ObjC and Swift is both acceptable. :-)
So, the box should primary try to align all content horizontal. If There's not enough horizontal space, do a "line break" and continue on the next "line".
This sounds like a job for UICollectionView with UICollectionViewFlowLayout. You can disable scrolling, and the layout object will tell you the size of the content so that you can adjust the size of the box.
(How do you compute the required width of such a view and decide when a line break is to be done (and how?))
If you're doing it yourself, you add up the widths of all the items on the first line, and if it's larger than the available space, you move the item that extends past the limit and any subsequent items to the next line. Repeat as needed. But that's exactly what a flow layout does for a collection view, so there's no need to roll your own.

Custom Collection View Layout like Chanel app

I'm trying to do a custom layout like the Chanel app you can find the app in the Appstore.
https://itunes.apple.com/us/app/chanel-fashion/id409934435?mt=8
I know they are using an UICollectionView, but no clue how to start.
The interaction feels like a tableview mixed with a paginated scroll. When you scroll the elements grow, and the first element position itself at the top.
Start with dragging & positioning just one UIView. See UIGestureRecognizer docs and look for existing examples of movable views. You'll need an UIPanGestureRecognizer to move the view.
Resize the view depending on its Y position.
Create & position an image inside that view depending on the view size using a couple autolayout constraints.
Note that Chanel app has different constants for these constraints. With a minimum view height, one image's top is 80% height, for another image it's 90% height. Make sure you can manipulate constraints from code (I think it's a good idea to create everything from code there, XIBs are not very flexible).
Make the view "anchoring" to certain points (e.g. top = -75%, 0%, 75%, 90% from what I see in the Chanel app) when you stop moving it. Just find the nearest one and animate the view to it.
Once you did it with 1 view, move all your work to an NSView subclass (if it's not yet there) and create a collection of these views.
You can create UICollectionView, but I'd rather do it with a simple NSArray: I actually don't see a reason to use UICollectionView here; it's a very simple structure. Anyway, you write your own gesture recognizer (don't you? I can't see another way) - so what's the point to use UICollectionView? If you want to expand the app functionality some day, UICollectionView will unlikely help you with that. Well, it's my hypothetical approach, you can find another one while working on that.
Position other views while you're moving an "active" view. Do it by hand, without any UIScrollViews.
Write a function that reflects the Y position of the "neighbor" views while you moving one. It should "slow down" to the bottom of screen.

Changing VoiceOver's scrolling/paging distance on a UITableView

I have a screen in my app that consists of a UITableView with another opaque view overhanging on the top (which is also interactive). The content offset is set correctly such that the entire tableView is visible to a sighted user.
However, when using VoiceOver and paging up/down with three fingers the default scroll distance will place part of the tableView under the overhanging view, and thus prevent VoiceOver from reading part of the tableView[1].
Is there any way to change how tall a "page" is when using VoiceOver?
[1]: I know that there are still ways to cycle through all the elements, etc, but it's far easier and more discoverable for partially sighted users to skim over elements with their finger.
I think you are looking for this: https://developer.apple.com/library/prerelease/ios/documentation/UIKit/Reference/UIAccessibilityAction_Protocol/index.html#//apple_ref/occ/instm/NSObject/accessibilityScroll:
Re implement that method and scroll the amount of pixels you require instead of a whole page

How do I create a view in iOS 5 which can scroll in any direction, like a map?

How do I build a freely scrollable view on iOS 5? I mean, I need to build a view which can scroll from left-right and top-down (any direction) and it will stay there. Inside this view, I need to place different kinds of objects.
(Imagine scrolling like a map. - I am using Storyboard.)
Yeah, UIScrollView.
And don't forget to set the content size !! (every noobs - including me - are always asking the same question, why my scrollView does not work?)
=> [scrollView setContentSize:CGSizeMake(1000, 2000)], for example.
EDIT :
I just saw your comment. I don't understand what you call "on top".
I made an app with custom maps, and I used an UIScrollView to make the job: it was pretty good at it.
You just have to add each scrollable element as subviews of your UIScrollView. And set the contentSize to fit to the subviews' frames, of course.

Resources