In my iPhone app, I would like the user to be able to "connect" two of my views by:
1) starting a drag in View A
2) as they drag towards View B, a straight line with one end in View A and the other end under at the current drag point, animates in a rubber-band fashion
3) when/if they release in View B, the line is then shown between the two views
I've seen examples of dragging and dropping views, and other examples of animations, but I haven't seen one that is a simple example of this kind of user-directed animation. Any pointers towards examples or the specific docs I should be looking at would be appreciated.
If this turns out to be trivial - my apologies. Although I've done quite a bit of development, I'm just getting started in the iPhone SDK and Core Graphics.
Turns out it is pretty easy - don't think of the line as belonging to either view, create a third view that is transparent and not opaque, place it over the top of the other two views. It could be full screen or you could calculate the size and position that just covers your views. Detect taps in this third view and use core animation to display a line from the point you started drawing to the point the line ends. When the line ends then you can detect whether the input was valid and place start/end points in the appropriate views. Functions that you will find especially useful in this process are UIView convertPoint:toView: and beginAnimations:context:.
It will probably keep things easier if you leave the line drawing as part of a dedicated view and add lines to it as they are accepted, rather than try to record the lines as part of the unrelated views that you are connecting with the lines - probably you want an array or similar containing CGFloats so that you can recreate the line view as necessary using drawRect:.
Related
I'm trying to build a custom, reusable UIScrollView that can be added to multiple views. The scroll view is going to be a weight picker. For the life of me, I can't find a decent example for how to implement this neatly or cleanly.
I would love for someone to point me to an existing library or tutorial that shows me how to do this. I've hacked apart a few examples, but so far, nothing is very good or reusable. Please help!!
For what it's worth, I have an image that individual ticks for the weight. So I can select to the tenth of the number (e.g. 160.4). The image has the first tick bold and larger than the remaining 9. I'd like to have the weight/number centered over the large tick. I'll update the points to my label/datasource after scrolling stops.
UPDATE
I need to make this. I have the custom font, background, and ticker image.
I would not do this through an UIScrollView. I think it would be more complex and you would certainly end up having issues when trying to add you custom picker into another scroll view.
What I would do is:
building the picker view by means of a series of CALayers, each one representing a "building block" of your picker view; see attached image:
each building block would represent a specific value by mixing a UILabel (the text) and an image;
use a pan gesture recognizer, or alternatively define touchesBegin/Moved/Ended method to deal with panning;
when a pan is done, you displace the view content to the left or right according to the panning;
when panning, you also add new building blocks to the left or right end of the picker to account for empty areas that would be revealed by the displacement done at point 4.
I think that having a look at another kind of custom control source code would be of great help to you. You would not possibly find your custom picker already implemented, but could get some guidance. Have a look then at cocoa controls.
Hope this helps.
If I were going to implement this, I would create a really wide image that had every weight on it I'd ever need - I would probably create this in code when the app started up. This image is then used as the contentView of your picker. You get all the scrolling features "for free", and you could even update the values shown in the other parts of the view during scrolling (or dragging.
The scrollView is just the area with the tick marks and weight numbers, and resides in a subview above the background, but below the centered vertical line that shows the actual weight.
EDIT: on second thought, forget the image. If you have the code to draw the image, you can do the drawing in a custom UIView. So you get the draw rect, you know the contentOffset, so you can draw just what you need.
What I'm trying to do:
The bottom (and top) cells of a "grouped style" UITableView have rounded corners - as is the default in many Apple iOS and non Apple apps. Sometimes cells are inserted via an animation, i.e:
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationTop];
Now this works fine. Except: When a cell is inserted below the bottom cell, or above the top cell (for a grouped style table) - there seems to be no smooth animation.
The Problem:
What I mean is that the corners of the top/bottom cells need to animate from rounded to unrounded (as they are no longer top/bottom) - and this happens in a very jerky and unlike Apple fashion.
Does Apple ever do this in an App - if so, how do they do this smoothly? Otherwise, how could this be done right?
NB - I know this is a pretty minor detail, but I'm a bit of a perfectionist...!
Take a look at the Weather App that comes with iOS, in the flip side view where you configure your cities you will see a table that accommodates these animations. As Mark Adams points out, in iOS5 there is an enum for the OS to auto-detect which animation should be used. If you want to target pre-iOS5 then on your delegate method that commits deletion you should determine what cell is being deleted based on its row position, if its 0 you would use UITableViewRowAnimationBottom if it is the the last row you would use UITableViewRowAnimationTop and if it is somewhere in the middle you would likely use UITableViewRowAnimationTop so that the group shifts upward with the animation, you might want to play around with this though.
Sorry I don't have time to write actual code out and test it, but maybe I can give you a lead. You're basically asking to set the corner radius on only two corners of a cell, then fluidly animate them to sharp corners as they move toward the center of the table.
One way to do that might be to define your custom UITableViewCell with a CAShapeLayer as the background. CAShapeLayer is drawn from Quartz paths: see http://developer.apple.com/library/IOS/#documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/dq_paths/dq_paths.html#//apple_ref/doc/uid/TP30001066-CH211 . CAShapeLayer's path is animatable, but not with implicit animation. You'll need to use CAPropertyAnimation or perform your own interpolation. In my experience, for example in http://itunes.apple.com/us/app/blaster-through-asteroid-field/id414827482?mt=8 , doing your own interpolation is faster and more reliable (but other than the interpolation and collision detection everything most of the game relies on quartzcore, so I don't think you'll have a performance issue).
SUMMARY: Custom cell has CAShapeLayer backing. CAShapeLayer has path with 2 rounded corners. You animate the path property as the position of cell moves toward a certain point on screen. Done, smoothly animated rounded-to-square corners. Let me know if you try it out :-)
Other references:
http://developer.apple.com/library/IOS/#documentation/GraphicsImaging/Reference/CAShapeLayer_class/Reference/Reference.html#//apple_ref/doc/uid/TP40008314
http://developer.apple.com/library/IOS/#documentation/GraphicsImaging/Reference/CGPath/Reference/reference.html
As there's no real (code complete) answer currently, here's a list of the best workarounds.
Change your interface so that this jerky animation never happens.
This might mean using a plain style tableView as opposed to grouped
style, or using the alternate style seen in the Weather app (see
Chris Wagner's answer. Thanks Chris!). It might even mean using a
completely different interface that doesn't incorporate this
animation - this was the solution I went for, I managed to work out
a UX alteration that is possibly even better than the original.
Deal with it. Apple seem to, in some of the less used parts of
the OS or their apps.
File a bug report on bugreport.apple.com. It would be nice if Apple
fixed this... I know it's a pretty minor detail, but that's why I
like Apple products - cos they get the little details right!
Create a custom UITableViewCell subclass. I've thought a bit about
this, and while it's a bit of a programming challenge, I think it's
doable. Here's how: EDIT: See Rab's answer for some more detail. Still haven't coded it out yet but may give it a go when I get some time.
The custom cell has a property that defines whether the cell is a top, middle, or bottom cell. This can be set by a UITableViewController (or subclass etc).
The backgroundView property of the custom cells can be custom drawn using quartz for the rounded corners etc.
Whenever the property that defines the position of the cell is altered, the change to backgroundView can be animated. The hard part
here is animating the curve of the corners... is this doable? Apple
seems to do it in the corners of notification center, when it is
pulled down and pushed up - so seems like it is possible.
If someone seriously want to do this, and put the code on gitHub... you would be awesome! And you would have bettered
Apple's UI ;)
Have you tried to insert two rows at a time? The one on the top with zero height, and the next with rounded corners (you can use quartz core for that)
If you check the Tags section of the 'Send to Youtube' part of the Photos app, you'll see apples animations are just as 'jerky' as the rest of ours. (obviously you need a video to upload to youtube)
I'll assume thats what you were thinking of...
I have a UITableView within a UIView that has a PNG of a rubber rope attached to the top of the tableview. It stays on top though when the table slides down, it's not a part of the table, just a part of the view.
Desired behavior:
when the user pulls the tableview down, I'd like somehow for this rope to extend realistically like a rubber band
when they let go of the table, the table cells extend back up with the same speed as the attached rubber band.
This is not for scrolling through the table view, but when you only have say 2 or 3 cells and you pull it down, and it extends a rope, and when you let go, it goes back up.
It's the same behavior as a default table view, but now there's just a rope attached to the top.
I'm not looking for any specific answers since I know that can be difficult, but if you have any idea what direction I should be heading in to accomplish this, please share.
I came up with two different ideas:
Using Images as transitions and stretching
My first idea is to create a bunch of images for each transition. Of course, not the entire animation, just 5 different states for the rubberband, something like this:
Each transition is then fit to the next one by stretching the image, and then swap to the next state until the last one is reached. This wouldn't look entirely realistic, but convincing enough, and enough to capture the attention of the user.
Using CGPathAddEllipseInRect
On the other hand, if stretching & swapping images doesn't work, you could draw an oval using CALayer methods, something like a CGPathAddEllipseInRect (sample code here), and drawing a close loop. This is more handy as it draws the entire transition.
I am working on a sample in which I have placed two textures one above the other. What I want, whenever user moves his finger on the screen, underneath view should get revealed as he moves. Wiping out front view to reveal underneath view is what I am looking for.
I would like to know some of ideas/ thoughts to implement this feature using OpenGL ES. Any related pointer will be highly appreciated.
Thanks in advance.
This does not sound performance-intensive so simple code can trump complicated tuned operations.
You don't need to use OpenGL. You can simply have two images - front and back - with the front supporting an alpha channel. Each time you get a hit or move, you clear a circular patch whereever the impact is for some certain radius or such.
And then queue-up a redraw. The redraw draws the two bitmaps, back first then front.
If possible, try to queue a redraw for just the the area where you have updated the front since the last draw.
I am creating a syntax highlighter for the iPhone and in order to display text with multiple formats, I have sub-classed UIView and modified the drawRect: method so that each line is displayed with the proper syntax highlighting (highlighting is done earlier with RegEx, text is drawn with CGContextShowTextAtPoint() one line at a time). Everything works ok, I store each line of text as an NSString in an NSMutableArray, I handle the keyboard through a hidden UITextField and its delegate methods, the cursor is a blinking CALayer that can be moved around with touches and I have a custom scroll view that handles scrolling. However, I have two problems that I can't seem to wrap my head around:
Word wrap, right now the text just keeps going off the left end of the screen. To keep things fast I only redraw the portions of the view that have changed (usually just the line being edited, but sometimes the lines below as well e.g. if you press return halfway through the document) with setNeedsDisplayInRect:. This makes word wrap complicated because then you have to draw more than one line on the screen, even though it still is only one object in the array.
UIViews have a maximum content size of 1024x1024 which equates to about 64 lines. I need the ability to display more than that. I am thinking about using multiple CALayers one after another, but I am having trouble drawing content to the layers (using drawLayer:inContext: and drawInContext:).
So my questions are:
Does anyone have any, even general, suggestions about how to accomplish either of these two points. Or,
Has someone already written a custom text-drawing view that handles these things that I could use instead.
Thanks,
Kyle
EDIT: The scrolling problem is pretty much solved, however I am still having trouble with word-wrap. My trouble is that everything is done by line: the view updates one line at a time, the text is stored as an array of lines, the highlighter highlights one line at a time, etc. and having a single index in the array (one line of text) take up multiple lines on the screen raises some problems, for example, I had to implement my own movable cursor and when you move the cursor it needs to be able to turn a display line (found by dividing touch.x by the line height) into a text line (an index in the array). Any ideas?
You should first spend some quality time understanding how this problem was solved on Mac:
http://developer.apple.com/documentation/Cocoa/Conceptual/TextArchitecture/Tasks/AssembleSysByHand.html
http://developer.apple.com/documentation/Cocoa/Conceptual/TextLayout/TextLayout.html
In particular, you should become familiar with line fragment generation, which is the problem you're trying to solve for word-wrap, and you should understand glyph generation in order to do rich text well. You don't need to implement all of the flexibility of NSTypesetter or NSLayoutManager of course, but the text system on Mac is incredibly powerful, and you should learn from its example. Implementing something similar to NSAttributedString for iPhone may be valuable to you and improve your performance.
Unless you're moving things around a lot (for which this would best work in a UIScrollView), I don't think you should need to use CALayers here. That seems overkill for the problem, and may actually adversely impact the optimizations already provided by UIScrollView. If you're seeing performance problems, first make sure you're not doing redundant calculations within your drawRect:.
Check out TTStyledText in Three20 library. Not sure how well it matches your goals, but might serve you as an example. (The library itself is a bit bloated, but is a wonderful source to look at.)
It may be best to draw your text within a CATiledLayer hosted within your UIView, in order to get around the 1024x1024 texture size limit (which appears to actually be 2048x2048 on all existing devices). For an example of text drawing within a CALayer, I'd refer you to the CPTextLayer class within the Core Plot framework. This layer (which inherits from the CPLayer class within that same framework) does cross-platform (Mac and iPhone) text rendering in a CALayer. You might be able to extend it to work as a CATiledLayer for longer text blocks.
One thing to be aware of is that platform-specific drawAtPoint: methods are used in this layer, instead of the CGContextShowTextAtPoint() function you are using. The reason for this is that CGContextShowTextAtPoint() only works on ASCII text, meaning that you can't do Unicode text rendering with it. There is an example of using CGContextShowTextAtPoint() to draw text, within a #define'd out portion at the bottom of the renderAsVectorInContext: method.