I'am working on an iPad application which can visualise network graphs with nodes and edges. This have I accomplished by using an UIScrollView and added UIViews as subviews for the nodes and CAShapeLayer as edges. This works ok with panning and tapping on nodes and uiscrollview, but now I want to implement zooming. This is where my problems begin. How am I supposed to use the UIScrollView with multiple UIView that should zoom in or out according to pinching? How should I limit the zooming to fit the UIScrollViews content size?
I have managed to create a structure as following:
Where the View under the scrollview function as a content view.
I add subviews to the content view programmatically so it would look like:
View
Scroll View (UIScrollView)
View (UIView)
>> Node1 (UIView)
>> Node2 (UIView)
>> Node3 (UIView)
The nodes works fine and can be dragged around. The problem (just to repeat my self) is that I can't control the content view according to the uiscrollview. I have tried to implement pinch gesture which works, but jumps up every time you begin a pinch. Further I have tried to implement a pan gesture but it will only work on the scrollview not on the content view.
I hope you guys can understand my problem. If you could help me to understand the structure of the views and where I should use gestures for pinch and panning I would really appreciated.
Actually, I am begining implementing some similar graph handling visualization.
I Assume, that solution will be a little more tricky, than simply uiview with nodes.
I think, there will be some OpenGL handling panning, taping and zooming in-out. - something with SpriteKit or SceneKit, i guess. I have seen he video from WWDC 2012, 2013, or 2014 - I don`t remember, where they showed how to deal with OpenGL and scrollviews.
Or it will be scrollview only to recieve touches, and some other view to handle animation for theese touch-drag-pinch events.
Related
Basically, I'm working on an app on the iPad that involves a line which the user can scroll horizontally through on the screen. It's set up so that there is a UIScrollView and a UIView on top that uses drawRect. I also have a UIView that's basically a ball which I can move around via touch, and also detects when it is within the UIScrollView and when it is on the line that is painted in the UIView.
Here's my main problem, though. Both the UIScrollView and the ball are subviews to the same view controller, and I want to make it so that, when the ball is dragged into the UIScrollView, that it will scroll along with it and even disappear if need be. How should I do this? should I just make the ball a subview of the UIScrollView when this happens, or should I do it some other way?
Use a container view. Put the ball in the transparent container subview and that is the view that moves around ( taking the ball with it). When you want the scroll view or line to move with the ball, insert them into the container at position 0 (under the ball).
I have created a small app using a ScrollView w/ paging and a series of UIImageViews each representing a page. It acts similar to the Photos.app.
I want to be able to pan and zoom individually for each image (page of a scrollview).
What would be the most sound approach to do this? Should I replace the UIImageView page with a scrollview with a UIImage inside it? I would then have a main scroll view where each page of the scrollview had a scrollview with an image inside it that could be pinched,etc.
It seems like a messy approach. I am looking for a clean approach. Any suggestions?
Well, as far as I know the approach you suggest is the way it is done. I have used this to create a PDF viewer and once everything is in place it doesn't feel too messy.
You might want to use CATiledLayer as your inner (per-page) scrollview's layer (instead of CALayer) if these images you have are really big (which could well be the case since otherwise zooming in on them would not bring you much but pixelation, but this is just an assumption on my part).
Checkout UIPageViewController. This controller manages a view controllers for each page and transitions between them using effects like page curl or scroll.
In your case you would create a view controller that manages a scrollview for zoom & pan for each page.
I have been looking to all the other similar topics here, using UIGestureRecognizers, using hitTest:withEvent, pointInside:withEvent: etc. but nothing seems to be ok for what I need to achieve.
Basically I have a main view (self.view of a common UIViewController) and a small rectangular UIScrollView attached onto it at the bottom: the scrollView is filled with some UIImageViews and the user can scroll it as usual.
But the user should also be able to drag one UIImageView (or a copy of it) from the UIScrollView to the main view, and, this is what I am finding really difficult, with the SAME dragging gesture, hence I need a way to:
1) Distinguish between normal horizontal scrolling gesture, which should be handled by the UIScrollView the usual way and a dragging gesture over the image view.
2) Once identified a dragging gesture, should propagate the touch to the superview, which will host a copy of the UIImageView and WITH the SAME dragging gesture continue the dragging over the main view even out of the bounds of the UIScrollView.
Please note that I know that if the UIScrollView has userInteractionEnabled = NO the touch is propagated to the subviews, but 1) I want to propagate it to the superview not the subviews, 2) the userInteractionEnabled property apparently becomes active only once the initial gesture is terminated, while I need to use a single dragging gesture.
Thank you very much for any help.
So, so far I have ended up implementing the touchesShouldBegin:withEvent:inContentView: method of my UIScrollView subclass but with delayContentTouches set to YES (default) instead of NO as #nhahtdh was suggesting.
Strangely enough even only implementing the method was sufficient for my subviews to intercept the dragging, and still my scrollview is scrolling properly, while with delayContentTouches set to NO I was not able to scroll it as all the subviews were starting to move around.
Really the credit for this is #nhahtdh, so man, if you post an answer I will accept it, thank you very much for your help.
Backstory
I have an iPad app that needs to allow the user to navigate through groups of images. Each group is laid out in its own vertical UIScrollView (paged) so the user can swipe up and down down to see each image. Each of the group UIScrollViews is placed in a single (only one exists in the app) outer horizontal UIScrollView (also paged). This works great.... I can swipe up and down to view the images in a group and swipe left and right to go to the next or previous group.
Problem
The problem started when I needed to add zooming for each image. I accomplished this by placing each image inside its own UIScrollView. When the image is zoomed I can pan around the image and when I get to the top or the bottom of the zoomed image the group's vertical UIScrollView pages to the next or previous image as expected. Unfortunately the outer horizontal scrollview will not page to the next group when the image is zoomed and I pan to the leftmost or rightmost edge.
Is there a better(more correct) approach than triple nesting UIScrollViews or can I somehow forward touches to the outer horizontal scrollview?
Any help or suggestions would be greatly appreciated.
hope i'm not too late but I think I have a solution for your problem.
Here you can find an Xcode project demonstrating the scrollview setup you have, your problem and the proposed solution: https://bitbucket.org/reydan/threescrollviews
Basically the solution was to add 1 pixel to the contentSize.width of the vertical scrollviews. This forces the vertical scrollview to scroll a little when you pan to the edge of the zoomed image. It scrolls a little and then continues to the next vertical scrollview.
If you download the project you will see that I've created some scrollviews in the viewDidLoad method. There, I create one horizontal scrollview containing 3 vertical scrollviews, each containing 5 images. Each image is actually incapsulated in a scrollview to enable per-image zooming. In total... triple nested scrollviews.
I've also left some colored borders so that I can easily see how each scrollview scrolls.
the magenta = horizontal scrollview
the white = vertical scrollview
the blue = the image scrollview (the one that contains the image and allows for zooming)
the red = the UIImageView
You will see that I've tagged each image scrollview with value 10. This is used in the implementation of - (UIView*)viewForZoomingInScrollView:(UIScrollView *)scrollView delegate method where I return nil unless the event came from one of the image scrollviews.
If you have any questions about the project I made feel free to ask.
In the end, I would like to say that this browsing method is a little quirky for me as I sometimes scroll in the unwanted direction. Often I think I flick my finger vertically only to find the scrollview going left or right because it interpreted some tiny horizontal movement I had.
The problem I found with paging enabled for both horizontal and vertical movement is that the scrollviews are direction-locked, or so it seemed to me.
EDIT:
Today I've investigated the problem even more. These are my conclusions:
it's not a problem with zooming, it's a problem with having larger content in the innermost scrollview than the visible area(you can try this by zooming or simply initializing the content size larger than the bounds). This enables panning inside the inner-most scrollview and completely changes the behaviour of the touch events.
the bounce for a scrollview flag affects the behaviour of the panning(dragging) gesture when it reaches the edges of the content. If bounces=false then your panning gesture will stop at the edge, not forwarding the drag event up the chain (and thus not scrolling the parent scrollviews to show you other images). If bounces=true then, when you reach the edge and continue to drag the events will be forwarded to the parent scrollview and that scrollview will also be dragged. However, I've found that the dragging while bouncing reduces the distance dragged by aproximately 50%. This also happens in the Photos app.
if you start the dragging while the innermost scrollview is at the edge of the content then the scrollview is smart and will forward all events to the parent scrollview.
for some reason, triple nested scrollviews are problematic as the events are simply not forwarded between the topmost and middle scrollviews while panning inside the innermost scrollview. I have no idea why.
My solution with that +1 pixel to the content size, partially solves the problem.
EDIT 2013
Boy, these scrollviews are something out of this world :(
After more than a year of searching (just kidding... it was actually 2 days) I think I found a good elegant solution to the triple nested scrollviews.
I created a test project here:
https://github.com/reydanro/TripleNestedScrollViews
Inside the app, there is a switch which you can use to test with/without the fix.
The setup I am using in my app is a little different than this question. I have 1 vertical paged scrollview. Inside it, I have multiple horizontal paged scrollviews.
Inside some of the horizontal scrollviews I have another vertical paged scrollview.
Without the fix, once you get to the page with the inner-most scrollview you are pretty much stuck there as the vertical scrolling gestures are not forwarded to the outer-most scroll.
The fix is a custom UIGestureRecognizer that you need to add to the inner-most scrollviews. This recognizer follows touch events and if it detects a drag beyond the contentArea, then it will temporarily disable the rest of the scrollview's recognizers. This is the only method I discovered to make the scrollview forward the events up the chain
The gesture recognizer code is very rough with limited customization but should get the job done. At the moment I am focused on the app I develop, but will continue to update the repository.
PS: I haven't tested what happens with zoom but I see no reason why this method should not work (or be adapted to work).
I need to implement an "infinite scrolling" timeline, where the pinch-to-zoom will change the scale of the timeline rather than zoom on the underlying view, almost exactly like the scroll view of the app iStreamer (see below). I don't think I can do this with UIScrollView and am considering implementing a custom UIView that draws the timeline and its contents.
Are there any classes/formulas/constants that can provide the physics simulation behind the "glass on liquid" effect of inertial scrolling?
iStreamer (above) has overlapping touchable elements and inertial scrolling. They might be doing this with a regular UIScrollView, but I don't know how to achieve the same effect. I need to add elements that could span very wide stretches (years or decades on a timeline).
Just use a UIScrollView, and don't allow zooming on the scrollview. Instead, add a pinch gesture recognizer which implements the desired behaviour for the pinch behaviour.
Use UIGestureRecognizer as jer suggested, otherwise this might help: http://macresearch.org/dynamics-scrolling