Different between UIView and UIControl when drag inside UIScrollView - iphone

I have a UIScrollView which contains some small UIView subclass. UIScrollView is scroll enabled, and I want each UIView can be dragged inside UIScrollView freely.
My UIView subclass has this method:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
if ([touch view] != self) {
return;
}
CGPoint touchPoint = [touch locationInView:self.superview];
originalX = self.center.x;
originalY = self.center.y;
offsetX = originalX - touchPoint.x;
offsetY = originalY - touchPoint.y;
[self.superview bringSubviewToFront:self];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
if ([touch view] == self) {
CGPoint location = [touch locationInView:self.superview];
CGFloat x = location.x + offsetX;
CGFloat y = location.y + offsetY;
self.center = CGPointMake(x, y);
return;
}
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
if ([touch view] == self) {
self.center = CGPointMake(originalX, originalY);
}
}
I found touchesCancelled:withEvent will be called each time I just drag UIView several pixels. But these codes will work correctly if it is subclass of UIControl.
Why?
Thanks in advance!

UIScrollView tries to determine what kind of interaction the user has in mind. If you tap a view inside a scroll view, that view gets the touch began. If the user then drags, the scroll view decides that the user wants to scroll, so it sends touchesCancelled to the view which first got the event. It then handles the dragging itself.
To enable your own dragging of subviews, you can subclass UIScrollView and override touchesShouldBegin:withEvent:inContentView: and touchesShouldCancelInContentView:.

Related

Manually passing touches Began to a newly created instance

I want an object to be created when I touch an UIView. But I want the new object to be able to moved without the finger having to be raised. I tried passing the touched event to the new object but it doesn't work.
Is there any way to do it?
You'll have to derive a subclass of the UIView, which will serve as a HolderView for all newly generated views. Also, once a new view is generated, the new view will move along with the finger even if its not raised.
Following code will do it, tweak it later as per your needs:
#interface HolderView : UIView
#property(nonatomic,retain)UIView *newView;
#end
#implementation HolderView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint touchPoint = [touch locationInView:self];
if (CGRectContainsPoint(self.bounds, touchPoint)) {
CGRect r = CGRectMake(touchPoint.x-50, touchPoint.y-50, 100, 100);
self.newView = [[[UIView alloc] initWithFrame:r]autorelease];
self.newView.backgroundColor= [UIColor cyanColor];
[self addSubview:self.newView];
}
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
if (self.newView!=nil) {
UITouch *touch = [touches anyObject];
CGPoint touchPoint = [touch locationInView:self];
self.newView.center = touchPoint;
}
}
#end
i think the touch is creating a problem at your end while you touch at some other object the view is also present so touch can not identified on your movable object for that you need to use one UIImageview over your UIView and then detect the touch and then you'll be able to achieve your desired output
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [[event allTouches] anyObject];
CGPoint imgPop = [touch locationInView:imgPopup];
if ([imgPopup pointInside:imgPop withEvent:event])
{
CGPoint imgReply = [touch locationInView:viewComment];
if ([viewComment pointInside:imgReply withEvent:event])
{
} else {
viewPopup.hidden = TRUE;
}
}
}
this is how i manage this code is for dismissing the view on touch but you can modify and use for your purpose.

touchesMoved reaching out of my view bounds

I have subclassed UIView and there initially my view will be in a default color and i need to fill some different color on touch (from x axis = 0 to user touched point),here the problem is touchesMoved even if i drag out of my self view bounds it is getting those points,how to restrict it to only for my self view bounds.
I googled & tried below snippets but of no luck
if([self pointInside:point withEvent:nil]){
[self fillColor];
}
My touchesMoved method is as below,
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint point = [touch locationInView:self];
endPoint = point;
NSLog(#"moved x: %f,y: %f",point.x,point.y);
if(CGRectContainsPoint([self frame], endPoint)){ // this also not working
[self fillColor];
}
}
Any help is appreciated in advance.
just set tag in viewDidLoad: method and use bellow logic..
fillColorView.tag = 111;
and use bellow logic in touchesMoved: method like bellow..
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *tap = [touches anyObject];
CGPoint pointToMove = [tap locationInView:fillColorView];
if([tap.view isKindOfClass:[UIView class]])
{
UIView *tempView=(UIView *) tap.view;
if (tempView.tag == 111){
[self fillColor];
}
}
}
hope this help you...
In your touchesMoved method, CGPoint point = [touch locationInView:self]; replcae self by the view in which you wants the touch to be worked.
self will get the complete view, you should pass your drawingView at there, so that it will detetc touch only on that view.

UIScrollview touchesbegan,touchesmoved,touchesended actions

I did this touch actions in UIView, ie,
in a uiview there are two or three subviews v1,v2,v3. I used the code below to place the image i1,i2,i3 in corresponding views and if I moves the touch the images move to that point in the view.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
if ([touch view] == v1)
{
CGPoint location = [touch locationInView:v1];
i1.center = location;
}
else if([touch view] == v2)
{
CGPoint location = [touch locationInView:v2];
i2.center = location;
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
if ([touch view] == v1)
{
CGPoint location = [touch locationInView:v1];
i1.center = location;
}
else if([touch view] == v2)
{
CGPoint location = [touch locationInView:v1];
i2.center = location;
}
}
Now I have to do this for a sequence of 28 images so I went for Uiscrollview but I cant get that please explain me clearly with code. thanks a lot in advance for your help.
You need to subclass UIScrollView to get touch events of UIScrollView.

UIView drag (image and text)

Is it possible to drag UIView around the iOS screen while it has both image and text? e.g. small cards. Could you point me to the similar (solved) topic? I haven't found any.
This is what a neat solution, based on pepouze's answer, would look like (tested, it works!)
- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *aTouch = [touches anyObject];
CGPoint location = [aTouch locationInView:self];
CGPoint previousLocation = [aTouch previousLocationInView:self];
self.frame = CGRectOffset(self.frame, (location.x - previousLocation.x), (location.y - previousLocation.y));
}
While UIView does not have a built-in support for moving itself along the user dragging, it should be not so difficult to implement it. It is even easier when you are only dealing with dragging on the view, and not other actions such as tapping, double tapping, multi-touches etc.
First thing to do is to make a custom view, say DraggableView, by subclassing UIView. Then override UIView's touchesMoved:withEvent: method, and you can get a current dragging location there, and move the DraggableView. Look at the following example.
-(void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *aTouch = [touches anyObject];
CGPoint location = [aTouch locationInView:self.superview];
[UIView beginAnimations:#"Dragging A DraggableView" context:nil];
self.frame = CGRectMake(location.x, location.y,
self.frame.size.width, self.frame.size.height);
[UIView commitAnimations];
}
And because all subviews of the DraggableView object will be moved, too. So put all your images and texts as subviews of the DraggableView object.
What I implemented here is very simple. However, if you want more complex behaviors for the dragging, (for example, the user have to tap on the view for a few seconds to move the view), then you will have to override other event handling methods (touchesBegan:withEvent: and touchesEnd:withEvent) as well.
An addition to MHC's answer.
If you don't want the upper left corner of the view
to jump under your finger, you can also override touchesBegan
like this:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *aTouch = [touches anyObject];
offset = [aTouch locationInView: self];
}
and change MHC's touchesMoved to:
-(void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *aTouch = [touches anyObject];
CGPoint location = [aTouch locationInView:self.superview];
[UIView beginAnimations:#"Dragging A DraggableView" context:nil];
self.frame = CGRectMake(location.x-offset.x, location.y-offset.y,
self.frame.size.width, self.frame.size.height);
[UIView commitAnimations];
}
you should also define CGPoint offset in the interface:
#interface DraggableView : UIView
{
CGPoint offset;
}
EDIT:
Arie Litovsky provides more elegant solution that allows you to ditch the ivar: https://stackoverflow.com/a/10378382/653513
Even though rokjarc solution works, using
CGPoint previousLocation = [aTouch previousLocationInView:self.superview];
avoids the CGPoint offset creation and the call to touchesBegan:withEvent:
Here is a solution to drag a custom UIView (it can be scaled or rotated through its transform), which can hold images and/or text (just edit the Tile.xib as required):
- (void) touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
{
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInView:self];
CGPoint previous = [touch previousLocationInView:self];
if (!CGAffineTransformIsIdentity(self.transform)) {
location = CGPointApplyAffineTransform(location, self.transform);
previous = CGPointApplyAffineTransform(previous, self.transform);
}
self.frame = CGRectOffset(self.frame,
(location.x - previous.x),
(location.y - previous.y));
}
This work for me. My UIView rotated and scaled
- (void) touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInView:self];
CGPoint previous = [touch previousLocationInView:self];
if (!CGAffineTransformIsIdentity(self.transform)) {
location = CGPointApplyAffineTransform(location, self.transform);
previous = CGPointApplyAffineTransform(previous, self.transform);
}
CGRect newFrame = CGRectOffset(self.frame,
(location.x - previous.x),
(location.y - previous.y));
float x = CGRectGetMidX(newFrame);
float y = CGRectGetMidY(newFrame);
self.center = CGPointMake(x, y);
}

Programmatically pass touches to UIScrollView to scroll

Basically I'm trying to make UIScrollView only scroll on higher angles. As in right now if you move your finger 10 degrees off horizontal, the scrollview will scroll. I'd like to push that up to, say, 30 degrees.
After doing some reading, I established the best way to do this would be to put a subclassed UIView on top of the scrollview. If the UIView on top's touches are above 30 degrees, pass it down to the scrollview, and otherwise don't.
However, I can't figure out how to pass the touches down. Here's my code right now:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSLog(#"glass touch began");
UITouch *touch = [touches anyObject];
beginning_touch_point = [touch locationInView:nil];
[scroll_view touchesBegan:touches withEvent:event];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
NSLog(#"glass touch ended");
UITouch *touch = [touches anyObject];
CGPoint previous_point = beginning_touch_point;
CGPoint current_point = [touch locationInView:nil];
float x_change = fabs(previous_point.x - current_point.x);
float y_change = fabs(previous_point.y - current_point.y);
if(x_change > y_change)
{
if(previous_point.x - current_point.x < 0)
{
[(MyScheduleViewController *)schedule_controller didFlickLeft];
}
else
{
[(MyScheduleViewController *)schedule_controller didFlickRight];
}
[scroll_view touchesCancelled:touches withEvent:event];
}
else
{
[scroll_view touchesEnded:touches withEvent:event];
}
}
I know right now that it's checking for 45 degrees, but that's not the important thing. What is important is that the touches are indeed getting passed down correctly to my scroll_view. I have it doing a NSLog() on touchesbegan and touchesended, and it's doing both correctly. It's just not scrolling. I'm worried touchesBegan and touchesEnded cannot cause a scroll. Does anyone know what can, or what I'm doing wrong?
I also tried to do the same but didn't succeed. It seems scrollView doesn't handle touchesMoved/touchesBegan, but it handles some other events to understand that user wants to scroll the view.
For me the solution was to determine thet shift value and set it explicitly as a scrollView content offset. It looked like this (I don't have the exact source code no, this code may work incorrectly):
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [touches anyObject];
CGPoint curr = [touch locationInView: self];
CGPoint offset = [scrollView contentOffset];
[scrollView setContentOffset: CGPointMake(offset.x + (curr.x - prev.x), offset.y) animated:YES];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
prev = [[touches anyObject] previousLocationInView: self];
}