I need to select text by swiping gesture without long press. There seems two way: One is to subclass UITextView or do something about UITextView, the other is to use Core Text to make a new UI component.
What should I do?
This is another answer with pan gesture.
Hope this is what you want.
- (IBAction)pan:(UIPanGestureRecognizer *)ges
{
CGPoint point = [ges locationInView:ges.view];
if (ges.state == UIGestureRecognizerStateBegan)
{
startPoint = point;
}
else if (ges.state == UIGestureRecognizerStateChanged || ges.state == UIGestureRecognizerStateEnded)
{
UITextPosition *start = [self.textView closestPositionToPoint:startPoint];
UITextPosition *end = [self.textView closestPositionToPoint:point];
UITextRange *range = [self.textView textRangeFromPosition:start toPosition:end];
[self.textView select:self.textView];
self.textView.selectedTextRange = range;
}
}
UIResponder contains
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;
Make a custom UITextView class and override these event could get what you want.
Hopes it could help you.
Just like baliman said.
A little modified.
First separate textView.text into array.
_swipeValue = [textView componentsSeparatedByString:#" "];
Seconde in gesture selector
if(gestureReconize.direction == UISwipeGestureRecognizerDirectionLeft) {
swipeIndex++;
swipeIndex = (swipeIndex >= [self.swipeValues count]) ? 0 : swipeIndex;
} else {
swipeIndex--;
swipeIndex = (swipeIndex < 0) ? [self.swipeValues count] - 1 : swipeIndex;
}
NSString *selectedValue = [self.swipeValues objectAtIndex:swipeIndex];
Third convert selectedValue to range of textView.text
NSRange range = [textView.text rangeOfString:selectedValue];
Final set textView.selectedRange
[textView select:self]; //See http://stackoverflow.com/questions/1708608/uitextview-selectedrange-not-displaying-when-set-programatically
textView.selectedRange = range;
It work. But not perfect if your textView.text has same word.
Hi Maybe something like this
//
// ViewController.m
// SwipeTextFieldDemo
//
#import "ViewController.h"
#interface ViewController ()
#property (weak, nonatomic) IBOutlet UITextField *swipeTextField;
#property (strong, nonatomic) NSMutableArray *swipeValues;
#end
int swipeIndex = 0;
#implementation ViewController
#synthesize swipeTextField = _swipeTextField;
#synthesize swipeValues = _swipeValues;
// Create list of colors
- (NSMutableArray *)swipeValues
{
if(_swipeValues == nil) {
_swipeValues = [[NSMutableArray alloc]initWithObjects:#"Red",
#"Blue",
#"Green",
#"Yellow",
#"Orange",
#"Pink",
#"White",
#"Black",
nil];
}
return _swipeValues;
}
- (void)swipeColor:(UISwipeGestureRecognizer *)gestureReconize
{
if(gestureReconize.direction == UISwipeGestureRecognizerDirectionLeft) {
swipeIndex++;
swipeIndex = (swipeIndex >= [self.swipeValues count]) ? 0 : swipeIndex;
} else {
swipeIndex--;
swipeIndex = (swipeIndex < 0) ? [self.swipeValues count] - 1 : swipeIndex;
}
self.swipeTextField.text = [self.swipeValues objectAtIndex:swipeIndex];
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Right swipe
UISwipeGestureRecognizer *swr = [[UISwipeGestureRecognizer alloc]
initWithTarget:self action:#selector(swipeColor:)];
[swr setNumberOfTouchesRequired:1];
[swr setDirection:UISwipeGestureRecognizerDirectionRight];
[self.swipeTextField addGestureRecognizer:swr];
// Left swipe
UISwipeGestureRecognizer *swl = [[UISwipeGestureRecognizer alloc]
initWithTarget:self action:#selector(swipeColor:)];
[swl setNumberOfTouchesRequired:1];
[swl setDirection:UISwipeGestureRecognizerDirectionLeft];
[self.swipeTextField addGestureRecognizer:swl];
self.swipeTextField.text = [self.swipeValues objectAtIndex:swipeIndex];
}
- (void)viewDidUnload
{
[self setSwipeTextField:nil];
[super viewDidUnload];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
#end
//
// ViewController.h
// SwipeTextFieldDemo
//
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController <UITextFieldDelegate, UIGestureRecognizerDelegate>
- (void)swipeColor:(UISwipeGestureRecognizer *)gestureReconize;
#end
Related
I want to add multiple UITapGestureRecognizer on UIScrollView but it recognise only one gesture.
I want to add first gesture for touch begin and second one for touch end event.
Following is my code:-
self.tapStartGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapGesture:)];
self.tapStartGesture.numberOfTapsRequired = 1;
self.tapStartGesture.numberOfTouchesRequired = 1;
[self.tapStartGesture setState:UIGestureRecognizerStateBegan];
[self.scrollView addGestureRecognizer:self.tapStartGesture];
self.tapEndGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapGesture:)];
self.tapEndGesture.numberOfTapsRequired = 1;
self.tapEndGesture.numberOfTouchesRequired = 1;
[self.scrollView addGestureRecognizer:self.tapEndGesture];
- (void)tapGesture:(UITapGestureRecognizer *)sender {
if(sender==self.tapStartGesture) {
NSLog(#"tapStartGesture");
} else if(sender==self.tapEndGesture) {
NSLog(#"tapEndGesture");
}
}
A tap gesture only has one state - "ended". You can't detect when a tap starts using a tap gesture. As you've seen, attempting to use two tap gestures doesn't accomplish what you want.
You need to implement the UIResponder methods touchesBegan and touchesEnded.
You may also want to see UITapGestureRecognizer - make it work on touch down, not touch up?
.
Issue solved by implement custom gesture.
File:-MyGesture.h
#import <UIKit/UIKit.h>
#interface MyGesture : UIGestureRecognizer
#end
File:-MyGesture.m
#import "MyGesture.h"
#implementation MyGesture
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
if (self.state == UIGestureRecognizerStatePossible) {;
self.state = UIGestureRecognizerStateBegan;
}
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
self.state = UIGestureRecognizerStateEnded;
}
#end
How to Use:-
MyGesture *gesture = [[MyGesture alloc] initWithTarget:self action:#selector(myGesture:)];
[self.scrollView addGestureRecognizer:gesture];
- (void)myGesture:(MyGesture *)sender {
if (sender.state == UIGestureRecognizerStateBegan) {
NSLog(#"tapStartGesture");
} else if (sender.state == UIGestureRecognizerStateEnded) {
NSLog(#"tapEndGesture");
}
}
When i run this program every time I touch the screen I would like the ball to move down. The problem is that every time I do that, the Swipe gesture(Moving Right) recognize the touch as well when I don't want it too. Which will cause the ball to move Sideway(Applied the touch and swipe).
Is there a way to ignore the Swipe gesture so my touchBegan is alone.
ViewController.h
int MoveBallRight;
int MoveBallDown;
#interface ViewController : UIViewController{
__weak IBOutlet UIImageView *Ball;
__weak IBOutlet UIButton *StartButton;
}
- (IBAction)StartButton:(id)sender;
#end
ViewController.m
#interface ViewController ()
#end
#implementation ViewController
- (IBAction)StartButton:(id)sender {
MoveBallRight = 30;
MoveBallDown = 30;
StartButton.hidden = YES;
UISwipeGestureRecognizer *swipeRight = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(handleSwipeRight)]; swipeRight.direction = UISwipeGestureRecognizerDirectionRight; [self.view addGestureRecognizer:swipeRight];
}
-(void)handleSwipeRight{
Ball.center = CGPointMake(Ball.center.x + MoveBallRight, Ball.center.y);
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
Ball.center = CGPointMake(Ball.center.x , Ball.center.y + MoveBallDown);
}
- (void)viewDidLoad {
[super viewDidLoad];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
#end
THis might help
swipeRight.cancelsTouchesInView = YES
i think if you only want to move your object downward, then instead of using Ball.center.x in your touchesBegan method use constant value for x
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
Ball.center = CGPointMake(<Use Constant here> , Ball.center.y + MoveBallDown);
}
and use
swipeLeft.cancelsTouchesInView=YES;
swipeRight.cancelsTouchesInView=YES;
I recently downloaded code for creating a calendar in xCode using objective-c. The code works for the most part, it highlights the days a user selects, but it is supposed to log out the start and end dates the user has selected. A delegate is used for this between my view controller and uiview (calendar view), but when my delegate is set in viewDidLoad, I noticed that it is always set to null. I am new to app making so would appreciate any help.
viewcontroller.m:
#import "CalendarViewController.h"
#import "DSLCalendarView.h"
#interface CalendarViewController ()<DSLCalendarViewDelegate>
#property (nonatomic, weak) IBOutlet DSLCalendarView *calendarView;
#property (weak, nonatomic) IBOutlet UIButton *selectDatesButton;
#end
#implementation CalendarViewController
- (void)viewDidLoad;
{
[super viewDidLoad];
// Do any additional setup after loading the view.
self.calendarView.delegate = self;
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
#pragma mark - DSLCalendarViewDelegate methods
- (void)calendarView:(DSLCalendarView *)calendarView didSelectRange:(DSLCalendarRange *)range;
{
if (range != nil) {
NSLog( #"Selected %ld/%ld - %ld/%ld", (long)range.startDay.day, (long)range.startDay.month, (long)range.endDay.day, (long)range.endDay.month);
}
else {
NSLog( #"No selection" );
}
}
calendarView.m (not all code, just methods using the delegate)
#import "DSLCalendarDayCalloutView.h"
#import "DSLCalendarDayView.h"
#import "DSLCalendarMonthSelectorView.h"
#import "DSLCalendarMonthView.h"
#import "DSLCalendarView.h"
#import "DSLCalendarDayView.h"
#interface DSLCalendarView ()
#property (nonatomic, strong) DSLCalendarDayCalloutView *dayCalloutView;
#property (nonatomic, copy) NSDateComponents *draggingFixedDay;
#property (nonatomic, copy) NSDateComponents *draggingStartDay;
#property (nonatomic, assign) BOOL draggedOffStartDay;
#property (nonatomic, strong) NSMutableDictionary *monthViews;
#property (nonatomic, strong) UIView *monthContainerView;
#property (nonatomic, strong) UIView *monthContainerViewContentView;
#property (nonatomic, strong) DSLCalendarMonthSelectorView *monthSelectorView;
#end
#implementation DSLCalendarView {
CGFloat _dayViewHeight;
NSDateComponents *_visibleMonth;
}
- (void)positionViewsForMonth:(NSDateComponents*)month fromMonth:(NSDateComponents*)fromMonth animated:(BOOL)animated;
{
fromMonth = [fromMonth copy];
month = [month copy];
CGFloat nextVerticalPosition = 0;
CGFloat startingVerticalPostion = 0;
CGFloat restingVerticalPosition = 0;
CGFloat restingHeight = 0;
NSComparisonResult monthComparisonResult = [month.date compare:fromMonth.date];
NSTimeInterval animationDuration = (monthComparisonResult == NSOrderedSame || !animated) ? 0.0 : 0.5;
NSMutableArray *activeMonthViews = [[NSMutableArray alloc] init];
// Create and position the month views for the target month and those around it
for (NSInteger monthOffset = -2; monthOffset <= 2; monthOffset += 1)
{
NSDateComponents *offsetMonth = [month copy];
offsetMonth.month = offsetMonth.month + monthOffset;
offsetMonth = [offsetMonth.calendar components:NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitWeekday | NSCalendarUnitCalendar fromDate:offsetMonth.date];
// Check if this month should overlap the previous month
if (![self monthStartsOnFirstDayOfWeek:offsetMonth]) {
nextVerticalPosition -= _dayViewHeight;
}
// Create and position the month view
DSLCalendarMonthView *monthView = [self cachedOrCreatedMonthViewForMonth:offsetMonth];
[activeMonthViews addObject:monthView];
[monthView.superview bringSubviewToFront:monthView];
CGRect frame = monthView.frame;
frame.origin.y = nextVerticalPosition;
nextVerticalPosition += frame.size.height;
monthView.frame = frame;
// Check if this view is where we should animate to or from
if (monthOffset == 0) {
// This is the target month so we can use it to determine where to scroll to
restingVerticalPosition = monthView.frame.origin.y;
restingHeight += monthView.bounds.size.height;
}
else if (monthOffset == 1 && monthComparisonResult == NSOrderedAscending) {
// This is the month we're scrolling back from
startingVerticalPostion = monthView.frame.origin.y;
if ([self monthStartsOnFirstDayOfWeek:offsetMonth]) {
startingVerticalPostion -= _dayViewHeight;
}
}
else if (monthOffset == -1 && monthComparisonResult == NSOrderedDescending) {
// This is the month we're scrolling forward from
startingVerticalPostion = monthView.frame.origin.y;
if ([self monthStartsOnFirstDayOfWeek:offsetMonth]) {
startingVerticalPostion -= _dayViewHeight;
}
}
// Check if the active or following month start on the first day of the week
if (monthOffset == 0 && [self monthStartsOnFirstDayOfWeek:offsetMonth]) {
// If the active month starts on a monday, add a day view height to the resting height and move the resting position up so the user can drag into that previous month
restingVerticalPosition -= _dayViewHeight;
restingHeight += _dayViewHeight;
}
else if (monthOffset == 1 && [self monthStartsOnFirstDayOfWeek:offsetMonth]) {
// If the month after the target month starts on a monday, add a day view height to the resting height so the user can drag into that month
restingHeight += _dayViewHeight;
}
}
// Size the month container to fit all the month views
CGRect frame = self.monthContainerViewContentView.frame;
frame.size.height = CGRectGetMaxY([[activeMonthViews lastObject] frame]);
self.monthContainerViewContentView.frame = frame;
// Remove any old month views we don't need anymore
NSArray *monthViewKeyes = self.monthViews.allKeys;
for (NSString *key in monthViewKeyes) {
UIView *monthView = [self.monthViews objectForKey:key];
if (![activeMonthViews containsObject:monthView]) {
[monthView removeFromSuperview];
[self.monthViews removeObjectForKey:key];
}
}
// Position the content view to show where we're animating from
if (monthComparisonResult != NSOrderedSame) {
CGRect frame = self.monthContainerViewContentView.frame;
frame.origin.y = -startingVerticalPostion;
self.monthContainerViewContentView.frame = frame;
}
self.userInteractionEnabled = NO;
[UIView animateWithDuration:animationDuration delay:0.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
for (NSInteger index = 0; index < activeMonthViews.count; index++) {
DSLCalendarMonthView *monthView = [activeMonthViews objectAtIndex:index];
for (DSLCalendarDayView *dayView in monthView.dayViews) {
// Use a transition so it fades between states nicely
[UIView transitionWithView:dayView duration:animationDuration options:UIViewAnimationOptionTransitionCrossDissolve animations:^{
dayView.inCurrentMonth = (index == 2);
} completion:NULL];
}
}
// Animate the content view to show the target month
CGRect frame = self.monthContainerViewContentView.frame;
frame.origin.y = -restingVerticalPosition;
self.monthContainerViewContentView.frame = frame;
// Resize the container view to show the height of the target month
frame = self.monthContainerView.frame;
frame.size.height = restingHeight;
self.monthContainerView.frame = frame;
// Resize the our frame to show the height of the target month
frame = self.frame;
frame.size.height = CGRectGetMaxY(self.monthContainerView.frame);
self.frame = frame;
// Tell the delegate method that we're about to animate to a new month
if (monthComparisonResult != NSOrderedSame && [self.delegate respondsToSelector:#selector(calendarView:willChangeToVisibleMonth:duration:)]) {
[self.delegate calendarView:self willChangeToVisibleMonth:[month copy] duration:animationDuration];
}
} completion:^(BOOL finished) {
self.userInteractionEnabled = YES;
if (finished) {
// Tell the delegate method that we've animated to a new month
if (monthComparisonResult != NSOrderedSame && [self.delegate respondsToSelector:#selector(calendarView:didChangeToVisibleMonth:)]) {
[self.delegate calendarView:self didChangeToVisibleMonth:[month copy]];
}
}
}];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
{
DSLCalendarDayView *touchedView = [self dayViewForTouches:touches];
if (touchedView == nil) {
self.draggingStartDay = nil;
return;
}
self.draggingStartDay = touchedView.day;
self.draggingFixedDay = touchedView.day;
self.draggedOffStartDay = NO;
DSLCalendarRange *newRange = self.selectedRange;
if (self.selectedRange == nil) {
newRange = [[DSLCalendarRange alloc] initWithStartDay:touchedView.day endDay:touchedView.day];
}
else if (![self.selectedRange.startDay isEqual:touchedView.day] && ![self.selectedRange.endDay isEqual:touchedView.day]) {
newRange = [[DSLCalendarRange alloc] initWithStartDay:touchedView.day endDay:touchedView.day];
}
else if ([self.selectedRange.startDay isEqual:touchedView.day]) {
self.draggingFixedDay = self.selectedRange.endDay;
}
else {
self.draggingFixedDay = self.selectedRange.startDay;
}
if ([self.delegate respondsToSelector:#selector(calendarView:didDragToDay:selectingRange:)]) {
newRange = [self.delegate calendarView:self didDragToDay:touchedView.day selectingRange:newRange];
}
self.selectedRange = newRange;
[self positionCalloutViewForDayView:touchedView];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
{
if (self.draggingStartDay == nil) {
return;
}
DSLCalendarDayView *touchedView = [self dayViewForTouches:touches];
if (touchedView == nil) {
self.draggingStartDay = nil;
return;
}
DSLCalendarRange *newRange;
if ([touchedView.day.date compare:self.draggingFixedDay.date] == NSOrderedAscending) {
newRange = [[DSLCalendarRange alloc] initWithStartDay:touchedView.day endDay:self.draggingFixedDay];
}
else {
newRange = [[DSLCalendarRange alloc] initWithStartDay:self.draggingFixedDay endDay:touchedView.day];
}
if ([self.delegate respondsToSelector:#selector(calendarView:didDragToDay:selectingRange:)]) {
newRange = [self.delegate calendarView:self didDragToDay:touchedView.day selectingRange:newRange];
}
self.selectedRange = newRange;
if (!self.draggedOffStartDay) {
if (![self.draggingStartDay isEqual:touchedView.day]) {
self.draggedOffStartDay = YES;
}
}
[self positionCalloutViewForDayView:touchedView];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
{
if (self.draggingStartDay == nil)
{
return;
}
DSLCalendarDayView *touchedView = [self dayViewForTouches:touches];
if (touchedView == nil) {
self.draggingStartDay = nil;
return;
}
if (!self.draggedOffStartDay && [self.draggingStartDay isEqual:touchedView.day]) {
self.selectedRange = [[DSLCalendarRange alloc] initWithStartDay:touchedView.day endDay:touchedView.day];
}
self.draggingStartDay = nil;
// Check if the user has dragged to a day in an adjacent month
if (touchedView.day.year != _visibleMonth.year || touchedView.day.month != _visibleMonth.month)
{
// Ask the delegate if it's OK to animate to the adjacent month
BOOL animateToAdjacentMonth = YES;
if ([self.delegate respondsToSelector:#selector(calendarView:shouldAnimateDragToMonth:)])
{
animateToAdjacentMonth = [self.delegate calendarView:self shouldAnimateDragToMonth:[touchedView.dayAsDate dslCalendarView_monthWithCalendar:_visibleMonth.calendar]];
}
if (animateToAdjacentMonth)
{
if ([touchedView.dayAsDate compare:_visibleMonth.date] == NSOrderedAscending)
{
[self didTapMonthBack:nil];
}
else
{
[self didTapMonthForward:nil];
}
}
}
if ([self.delegate respondsToSelector:#selector(calendarView:didSelectRange:)])
{
[self.delegate calendarView:self didSelectRange:self.selectedRange];
}
}
calendarView.h
#import "DSLCalendarRange.h"
#import "NSDate+DSLCalendarView.h"
#protocol DSLCalendarViewDelegate;
#interface DSLCalendarView : UIView
#property (nonatomic, weak) id<DSLCalendarViewDelegate>delegate;
#property (nonatomic, copy) NSDateComponents *visibleMonth;
#property (nonatomic, strong) DSLCalendarRange *selectedRange;
#property (nonatomic, assign) BOOL showDayCalloutView;
#property (nonatomic, assign) BOOL daysSelected;
+ (Class)monthSelectorViewClass;
+ (Class)monthViewClass;
+ (Class)dayViewClass;
- (void)setVisibleMonth:(NSDateComponents *)visibleMonth animated:(BOOL)animated;
- (void)commonInit;
#end
#protocol DSLCalendarViewDelegate <NSObject>
#optional
- (void)calendarView:(DSLCalendarView*)calendarView didSelectRange:(DSLCalendarRange*)range;
- (void)calendarView:(DSLCalendarView *)calendarView willChangeToVisibleMonth:(NSDateComponents*)month duration:(NSTimeInterval)duration;
- (void)calendarView:(DSLCalendarView *)calendarView didChangeToVisibleMonth:(NSDateComponents*)month;
- (DSLCalendarRange*)calendarView:(DSLCalendarView*)calendarView didDragToDay:(NSDateComponents*)day selectingRange:(DSLCalendarRange*)range;
- (BOOL)calendarView:(DSLCalendarView *)calendarView shouldAnimateDragToMonth:(NSDateComponents*)month;
I have a RootViewController with a UIScrollViewController (boardScrollView).
This boardScrollView has a UIImageView as subview (boardImage) to create the board. I can zoom in and out and scroll on the boardImage within the boardScrollView. Works great!
Now I want to drag & drop other UIImageViews into the boardScrollImage within the boardScrollView and also OUT of the boardScrollView.
For these other UIImageViews (Tiles) I have created a subclass of the UIImageView class (TileViewClass).
I have the drag&drop working to drop the Tile INTO the boardScrollView/boardImage and also drag&drop INSIDE the boardScrollView/boardImage but I can not get the drag&drop to OUTSIDE the boardScrollView working..
I think this is because I can not access the views in the rootviewcontroller from the subclass.
Maybe it is even better to place the Tile back to the topview (window) in touchesBegan, and so the determination of the drop-position is always done from the same view.
But I don't know how this can be done...
I have tried [[UIApplication sharedApplication].keyWindow bringSubviewToFront:self.dragObject]; in the touchesBegan method, but this does not do the trick....
Maybe I am missing a removeFromSuperView somewhere?
Anyone any idea how I can get the drag&drop working?
RootViewController.h:
#interface RootViewController : UIViewController <UIScrollViewDelegate>
#property (nonatomic, strong) IBOutlet UIScrollView *boardScrollView;
#property (nonatomic, strong) UIImageView *dragObject;
#property (nonatomic, assign) CGPoint touchOffset;
#property (nonatomic, assign) CGPoint homePosition;
#property (nonatomic, strong) UIImageView *boardImage;
#end
RootViewController.m:
#implementation RootViewController
#synthesize boardScrollView;
#synthesize dragObject;
#synthesize touchOffset;
#synthesize homePosition;
#synthesize boardImage;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
UIImage *image = [UIImage imageNamed:#"greyblue_numbered_15x15_900x900.png"];
self.boardImage = [[UIImageView alloc] initWithImage:image];
self.boardImage.frame = (CGRect){.origin=CGPointMake(0.0f, 0.0f), .size=image.size};
[self.boardScrollView addSubview:self.boardImage];
self.boardImage.userInteractionEnabled = YES;
self.boardScrollView.contentSize = image.size;
self.boardScrollView.canCancelContentTouches = NO;
self.boardScrollView.userInteractionEnabled = YES;
self.boardScrollView.clipsToBounds = YES;
}
TileImageView.h:
#import <UIKit/UIKit.h>
#interface TileImageView : UIImageView
#property (nonatomic, strong) UIImageView *dragObject;
#property (nonatomic, assign) CGPoint touchOffset;
#end
TileImageView.m:
#import "TileImageView.h"
#implementation TileImageView
#synthesize dragObject;
#synthesize touchOffset;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
self.exclusiveTouch = YES;
self.userInteractionEnabled = YES;
}
return self;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
if ([touches count] == 1) {
// one finger
CGPoint touchPoint = [[touches anyObject] locationInView:self.superview];
for (UIImageView *iView in self.superview.subviews) {
if ([iView isMemberOfClass:[TileImageView class]]) {
if (touchPoint.x > iView.frame.origin.x &&
touchPoint.x < iView.frame.origin.x + iView.frame.size.width &&
touchPoint.y > iView.frame.origin.y &&
touchPoint.y < iView.frame.origin.y + iView.frame.size.height)
{
self.dragObject = iView;
self.touchOffset = CGPointMake(touchPoint.x - iView.frame.origin.x,
touchPoint.y - iView.frame.origin.y);
[self.superview bringSubviewToFront:self];
}
}
}
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
CGPoint touchPoint = [[touches anyObject] locationInView:self.superview];
CGRect newDragObjectFrame = CGRectMake(touchPoint.x - touchOffset.x,
touchPoint.y - touchOffset.y,
self.dragObject.frame.size.width,
self.dragObject.frame.size.height);
self.dragObject.frame = newDragObjectFrame;
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UIView *iView in self.superview.subviews) {
if ([iView isMemberOfClass:[UIScrollView class]])
{
CGPoint touchPointScreen = [[touches anyObject] locationInView:[UIApplication sharedApplication].keyWindow];
if (touchPointScreen.x > iView.frame.origin.x &&
touchPointScreen.x < iView.frame.origin.x + iView.frame.size.width &&
touchPointScreen.y > iView.frame.origin.y &&
touchPointScreen.y < iView.frame.origin.y + iView.frame.size.height)
{
for (UIView *iView2 in iView.subviews) {
[iView2 addSubview:self.dragObject];
CGPoint touchPointImage = [[touches anyObject] locationInView:iView2];
self.dragObject.frame = CGRectMake(touchPointImage.x - touchOffset.x,
touchPointImage.y - touchOffset.y,
self.dragObject.frame.size.width,
self.dragObject.frame.size.height);
}
}
self.dragObject = nil;
}
}
Basically you'r catching all touch events on the tileimageview and moving its origin CGPoint around.
If a touch event ended, your cycling though all subviews of your tileviews superview.
If any1 of this is a UIScrollview, you try to locate if the touchpoint matches its frame.
So basically you just checking the frame of your uiscrollview. Therefore your touchpoint cannot be outside it.
Solution: you have to check the view in which you want to drop the tileimageview! you can assume that its a sibling of your uiscrollview(what i assume).
Other way, which i would recommend: Create a View - dropgroundview, in which you catch the touches(=> implement touchesend)
there you can drop your views around and check for subclasses of e.g. dropviews.
Solution: I have delegated the touch methodes to the RootViewController. There, I have access to all subviews and most important: the highest view.
Here, I say:
[self.view bringSubviewToFront:self.tmpDragObject];
And then it works!
Somebody help!
Here is what I want to implement :
While an UIImageView is becoming alpha 0 (hidden)
it can be touched
so that its alpha becomes one (unhidden).
But UIImageView is not touched while it is animating (=becoming alpha 0).
I tried hundred skills at stackoverflow, but didn't work.
They are.......
UIViewAnimationOptionAllowUserInteraction
setUserInteractionEnabled:YES;
touchesBegan
GestureRecognizer options
etc..
Only function 'hitTest' worked but not during animation.
Please reply . Thank you.
Below is my codes.
#import "ViewController.h"
#define AD #"text.png"
#import <QuartzCore/QuartzCore.h>
#interface ViewController ()
#end
#implementation ViewController
#synthesize scrollData=_scrollData;
#synthesize adImage=_adImage;
- (void)viewDidLoad
{
//_adImage is UIImageView
_adImage=[[adImage alloc] initWithImage:[UIImage imageNamed:AD]];
_scrollData.scrollView.delegate = self;
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(singleTapGestureCaptured:)];
[_adImage addGestureRecognizer:singleTap];
[_adImage setMultipleTouchEnabled:YES];
[_adImage setUserInteractionEnabled:YES];
[self.view addSubview:_adImage];
_adImage.alpha=0;
[_adImage setUp_pt:CGPointMake(160,250)];
_adImage.center=_adImage.up_pt;
[super viewDidLoad];
[self hideImage:_adImage delay:0];
[self becomeFirstResponder];
}
- (void)hideImageComplete:(UIView*)v
{
[self hideImage:v delay:0];
}
- (void)hideImage:(UIImageView*)v delay:(int)nDelay
{
[_adImage becomeFirstResponder];
[UIView animateWithDuration:1
delay:nDelay
options:
(UIViewAnimationOptionCurveEaseIn | UIViewAnimationOptionAllowUserInteraction)
animations: ^
{
_adImage.alpha=0.0f;
}
completion:^(BOOL completed){
[self hideImageComplete:v];
}];
}
- (void)singleTapGestureCaptured:(UITapGestureRecognizer *)gesture
{
NSLog(#"Gesture event on view");
_adImage.alpha=1;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch{
return YES;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
NSLog(#"hit!!");
[super touchesBegan:touches withEvent:event];
UITouch *touch = [touches anyObject];
CGPoint touchPoint = [touch locationInView:self.view];
CGPoint touchLocation = [touch locationInView:self.view];
_adImage.alpha=1;
}
#end
#implementation adImage
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint touchPoint = [touch locationInView:self];
if ([[[self layer] presentationLayer] hitTest:touchPoint]) {
[self.layer removeAllAnimations];
}
}
-(UIView*)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
if ([[self.layer presentationLayer] hitTest:point]) {
NSLog(#"hit!!");
self.alpha=1;
return self;
}
return [super hitTest:point withEvent:event];
}
#synthesize up_pt;
#end
Here is my ViewController.h codes.
#import <UIKit/UIKit.h>
#interface adImage : UIImageView {
}
//#property (strong, nonatomic) UIImageView *imageView;
#property (assign)CGPoint up_pt;
#end
#interface ViewController : UIViewController <UIScrollViewDelegate>{
}
- (void)hideImageComplete:(UIView*)v;
- (void)hideImage:(UIView*)v delay:(int)nDelay;
#property (strong, nonatomic) adImage *adImage;
#property(nonatomic, strong) IBOutlet UIWebView *scrollData;
#end
I have the answer for you, but first:
always name a class with a starting capital letter, ie AdImageView (its an image view not a pure view subclass too)
I took your code but commented out all your touch methods, which you may or may not need
the root issue here is that a UIGestureRecognizer won't work if alpha is 0, so you will see the animation to alpha=0 is broken into two pieces, almost to 0, then to 0. If the user taps to cancel, then the final completion block returns the view to 0
Code:
- (void)hideImage:(UIImageView*)v delay:(int)nDelay
{
//[_adImage becomeFirstResponder];
[UIView animateWithDuration:(3.0f - 0.1f)
delay:nDelay
options: (UIViewAnimationOptionCurveEaseIn |
UIViewAnimationOptionAllowUserInteraction)
animations: ^
{
_adImage.alpha=0.1f;
}
completion:^(BOOL completed)
{
NSLog(#"completed=%d", completed);
if(completed) {
[UIView animateWithDuration:0.1f animations:^{
_adImage.alpha=0.0f;
}];
} else {
_adImage.alpha=1;
}
}];
}
- (void)singleTapGestureCaptured:(UITapGestureRecognizer *)gesture
{
NSLog(#"Gesture event on view");
[_adImage.layer removeAllAnimations];
}
This worked great for me on iOS7.1+:
self.viewWithTapgesture.backgroundColor = [UIColor colorWithWhite:1.0 alpha:0.01];