UIGestureRecognizer for Rotation, Resize, Move On Edges of UIView - iphone

I want my UITextView to work and look similar to a crop utility (see image example below) but I cannot find the right approach how to use view's edges to move, rotate, resize my view:
I successfully applied UIGestureRecognizers to my entire UITextView to move (or pan) and rotate it, but when business comes to the edges functionality where all 4 nodes would either move, rotate or resize the view, I get stuck because nodes are hard to make fully functional. I tried 2 approaches:
1. Added all 4 nodes as UIImageView's as subviews of UITextView. When nodes were half outside UITextView edges I used clipsToBound = NO; to make them displayed right. However, nodes outside the view are not responding for touch. Then I tried solution 2...
2. Wrapped UITextView within container UIView and added 4 nodes for each edge and UITextView properly positioned inside. With this I achieved good UI but gesture recognizers still did not function because they were probably nested too deeply within a view which is a subview of another view that already had gesture recognizer. This needs more investigation but I feel like this approach a bit too complicated.
Am I on the right way? Perhaps, UIGestureRecognizer is not the right solution for this goal I am trying to achieve. If someone had experience working with anything similar please point me in the right direction. I am not looking for code rather ideas...

Approach 1 won't work (without some hacking) because a UIView's hit test does not respond to touches outside of its bounds. (For the hack see interaction beyond bounds of uiview)
Approach #3 is correct, and you should continue pursuing it. However, you could try using touchesBegan:withEvent: on your custom UIView subclass.
My ideas:
I have implemented a similar resizing control using the technique below. You have a little bit more control than a gesture recognizers, because normal UIGestureRecognizers tend to have some minimum distance at which they begin responding, which in not ideal in this case, since you want near instant response. In addition, you are less likely to have conflicts, knowing that you already have some gesture recognizers.
Many would probably recommend that you design your own UIGestureRecognizer to encapsulate and solve some of the issues I outlined above. This is probably a cleaner approach, but you will just need to be implement several UIGestureRecognizer delegate methods such as
gestureRecognizerShouldBegin: and gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer: to prevent simultaneous interaction with other gesture recognizers.
You may also need to require some gesture recognizers only activate when others fail, using requireGestureRecognizerToFail:.
Maybe you could start with #1, get it working, and then, ideally, refactor it into #2.
Sample code for idea 1
typedef enum {
HandleTopLeft,
HandleTopRight,
HandleBottomLeft,
HandleBottomRight
}
HandlePosition; //you will need fewer
#protocol SizingHandleDelegate;
#interface SizingHandle : UIView
#property (nonatomic, assign) id<SizingHandleDelegate> delegate;
#property (nonatomic, assign) HandlePosition position;
#end
#protocol SizingHandleDelegate <NSObject>
- (void)touchesBeganForSizingHandle:(SizingHandle*)sizingHandle;
- (void)touchesEndedForSizingHandle:(SizingHandle*)sizingHandle;
#end
Your SizingHandle class should override touchesBegan:withEvent: and touchesEnded:withEvent:: and forward those messages to their delegate. The delegate should then be able to apply the correct logic.
Also, you might want to handle the use case of the user touching more than one SizingHandle at a time.

When I had such a requirement, I tried to implement this using one of the sample codes from the below link, It really helped.
CropImageSampleCodes

Related

Making multiple moving images respond to taps

In the app, I have several images (same shape and size, different colors) that move on the perimeter of a circle, kinda like cursors acting as compass needles. I want to be able to tap each of these and then display a message based on which one is tapped, but am not sure what kind of approach would be good for this. Right now I'm trying making them in to UIButtons, but it's giving me a lot of hassle. Is there a way to do this with a UITapGestureRecognizer? I doubt it's possible to have one of those on the screen, but have it keep track of 6 different moving areas and let other tap events through, and I don't think adding 6 different recognizers is a good idea, so I'm just wondering if anyone has suggestions on how to go about this. I'm using Core Graphics.
The best method to achieve this effect would probably be to subclass a UIButton (or even UIControl if you'd like) that has the method that overrides touch. For example if you were to subclass UIControl you could override the method below to detect touches and do what you wish with them:
-(BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event
Then whatever you want it to look like could be done in the drawRect: method by overriding that (and uncommenting it). Same goes for subclassing anything and tracking touches within the subclass: UIButton, UIImageView, UIControl, etc.

Double slide to unlock for iPhone?

I want a control for iOS that slides left-to-right, similar to slide to unlock, but also slides right-to-left. I want something very similar to the control on Android's lock screen, where sliding left-to-right unlocks and sliding right-to-left silences.
It seems like this would be easiest to implement by subclassing UISlider, right? Would I need two sliders, or should I do it with just one?
Are there already any open source versions of this?
Ideas
Here's an idea of what the protocol might look like:
#interface DoubleSlider : UISlider
typedef enum {
DoubleSliderDirectionLeftToRight,
DoubleSliderDirectionRightToLeft
} DoubleSliderDirection;
#end
#protocol DoubleSliderDelegate <NSObject>
#optional
- (void)doubleSlider:(DoubleSlider *)doubleSlider
didBeginSlidingWithDirection:(DoubleSliderDirection)direction;
- (void)doubleSlider:(DoubleSlider *)doubleSlider
didEndSlidingWithDirection:(DoubleSliderDirection)direction;
#end
Related
iPhone "slide to unlock" animation
Slider which helps to unlock the iPhone
UISlider, slide to unlock
iPhone:Programming UISlider to position at clicked location
Custom Double Handle Slider
I can't think of any third party versions for this. I don't feel that UISlider would be a good place to begin. If I were you, I would subclass a UIScrollView instead.
Set it up at the bottom of the screen to have a height of 44 pixels or so. Lay down the images at the appropriate positions on the scrollView like in the image you posted.
Then, conform to the UIScrollViewDelegate and implement the scrollViewDidScroll: method to track when the user slides it left or right. Inside this method, check the contentOffset property of the UIScrollView. By trial and error, you can find out the exact content offsets where you feel that sliding the scrollview should trigger the actions you're looking for.
For example, if the contentOffset being 50 means that it's for DoubleSliderDirectionLeftToRight, you can fire off the delegate callback and then have the scrollview 'jump back' to the original position by using scrollRectToVisible:animated:
Sorry for the long winded and RTFM-ish answer, but I have no code to support this. This is just speculation, but I don't see why this solution would not work :)

Slide of UIScrollView

I made a slideview using a uiview and detecting touches to move pages. This slideview is almost like this, except that I made it works like a UITableView.
Now I'm using this to uivews with uiscrollviews. The problem is, "how to distribute touch events to scrollview or slideview?". I had the logic to do. Basically, the uiscrollviews are vertical and slideview is horizontal.
I tried hitTest to keep the touchBegan,Moved,Ended in slideview. When I get a touch movement horizontally, I keep to slideview, when vertically, distribute to uiscrollview. But I cannot figure out how to distribute events to uiscrollview.
Calling [scrollView touchesBegan:touches withEvent:event] doesn't work. I supposed uiscrollview has a different way to work.
If you don't find a clue to your answer, probably, you're wrong.
UIScrollView uses a own way to get touchesBegan, Moved and Ended. Way that I don't, but it's mean if you override touchesBegan to make UIScrollView stops to work, you won't get it. Using hitTest in superview of scroll, you can get the touches before UIScrollView but you can't change the touches target while touches is happening.
After all, there is one way to solve this, ashly, three ways.
1- Simulate touches
I didn't test this, you'll know below. Events come from UIWindow and distributed to subview by - (void)sendEvent:(UIEvent *)event. We don't know how touches target is saved, and change this is completely out of question. But we can use the idea of override superview's hitTest to know what the user will do to make a 'WA' to change the target. To do this, simulate a event of touch ended. Supposed target will be reset. Simulate a event of touch begin again, and this time make sure to let hittest get scrollview.
You can find how simulate events here. The problem is, probably your app will be rejected due using private methods.
2- Make your own UIScrollView
This should be the best or the worst, depending what you want to do. I believe it's painful. And isn't what you want to do right now.
3- Surrender to 'Nest UIScrollView'
To make slideshow of pdf, hq, docs and books, it's the best and painless way. Put a UIScrollView inside another and let them reach an agreement of scrolling. http://developer.apple.com/library/ios/#documentation/WindowsViews/Conceptual/UIScrollView_pg/NestedScrollViews/NestedScrollViews.html

iPhone - Catch horizontal swipes -before- subview

I have a view, that is able to go back to the previous view.
Let's say this is a questionnaire. So my main view is the questionnaireView(Controller), and it has a subview which shows a question with 2 possible answers. When one answers the question, one presses next, and the questionnaireView(Controller) shows the next question in that particular subview. Simple, right?
Okay, now imagine having to accomodate up to 10 anwers for a particular question. This will require implementing a scrollview in the subview, to accomodate for the question + all answers.
Now my question: I want questionnaireView(Controller) to receive notice of horizontal swipes (next/previous question), but I want all other touches (taps, for the radiobuttons of the answers, and vertical swipes for the scrollview) to go through...
Any idea's how to go about this? I have tried about 5 different approaches and broke my head and motivation on each on of 'em :/
Last one (and most simple one), was simply adding another subview to questionnaireView(Controller), overlaying the question+answer view.
This kindly catches all the touchesBegan/Moved/Ended/Cancelled for me, but even if I just put a forward in -each- of thoses methods ([self nextResponder] ...) the underlying view (with the answers, and the scrollview) won't respond anymore...
I'm kinda lost on this, and am thinking of writing my own Scrollview someday, since UIScrollView is the most terrible monster faced by iPhone devvers :P
Mind you, I am supporting iPhone OS 3.0 and up, so the new gesture APIs are no-go.
I'm not sure what you mean by "a forward in -each- of thoses methods". I'm not sure that nextResponder is the correct thing to forward them to either.
The problem is that touches are supposed to be "owned" by a single view throughout their lifetime. I'm not sure how UIScrollView or gesture recognizers are implemented, but I'm pretty sure they do more than you're supposed to do on your own.
I'd override questionnaireView's hitTest:withEvent: to return self. In your touchesBegan:withEvent:, call [super hitTest:[touch locationInView:self] withEvent:event] and store the subview that "owns" it. In touchesMoved:withEvent:, forward the touch to the relevant subview, but if you detect a gesture, "forget" the subview that owns the touch and instead call touchesCancelled:withEvent:. In touchesEnded/Cancelled:withEvent:, forward it to the subview and then forget the owning subview.
It's icky, but it mostly works. Some things it gets wrong (from the perspective of subviews):
-[UIEvent touchesForView:] will return the QuestionnaireView.
UITouch.view will return the QuestionnaireView
UITouch.phase might not be UITouchPhaseCancelled in touchesCancelled:withEvent: (if you detect the gesture and cancel the touch).
*

Architecting a visual grid of 'buttons'

I'm trying to design how to implement a relatively simple application.
I want to create a grid of 'squares' that cover an entire screen. Each square should have the ability to respond to touch events, and then have an on/off state. For example, if I touch an 'off' square, and then drag my finger across 10 other squares, I want them all to turn on. And vice versa.
I'm not sure of the memory overhead of just creating a grid of 150 buttons. Also buttons don't have a settable state, from what I can see. I was also thinking of subclassing UIView and implementing UIResponder methods. It feels like I should be creating an array of array of buttons (or subclass of UIViews), but I'm not sure if that's possible.
I'm assuming that I can tell what square I'm on by getting the location of the touchevent from the UIResponder methods. Do I need to create my own version of a myButton by subclassing UIView, and have a on/off state property, along with UIResponder methods, and then create an array of myButtons?
UISwitch is the only thing that does this at the moment, though some have had good experiences using the UISegmentedControl for this as well.
Beyond that, you'll have to change the style/color of a regular button or image in code, which is what a lot of application developers do so it looks and reacts exactly the way they want it to.
Unless you need more of UIView's event handling stuff, you'll get the best performance if you use a single view and give it a -touchesBegan:withEvent, -touchesMoved, and -touchesEnded methods. Then use a custom drawRect method to draw your individual squares in either on or off states. You could also use layers, but trying to lay out 150 views is asking for trouble.