Keeping the contents of a scaled NSScrollView centered and visible when resizing the window - objective-c

I am trying to magnify an NSScrollView which contains NSTextView and keep it centered to its content at all times. The NSTextView has left/right insets to keep the word wrapping consistent and to keep the paragraphs nicely at the center of the view.
Both [NSScrollView scaleUnitSquareToSize:...] and setMagnification:... have their own quirks and problems, but for now setMagnification seems a better option, as it is not relative.
Here's what happens (among other strange stuff):
On resizing, I update the insets:
CGFloat inset = self.textScrollView.frame.size.width / 2 - _documentWidth / 2;
self.textView.textContainerInset = NSMakeSize(inset, TEXT_INSET_TOP);
self.textView.textContainer.size = NSMakeSize(_documentWidth, self.textView.textContainer.size.height);
Zooming in:
CGFloat magnification = [self.textScrollView magnification];
NSPoint center = NSMakePoint(self.textScrollView.frame.size.width / 2, self.textScrollView.frame.size.height / 2);
if (zoomIn) magnification += .05; else magnification -= .05;
[self.textScrollView setMagnification:magnification centeredAtPoint:center];
Everything kind of works for a while. Sometimes, depending on from which window corner the window is resized, the ScrollView loses its center, and I haven't found a solution for re-centering the view of a magnified NSScrollView.
After magnification, layout constraints can get broken too when resizing the window, especially when the textContainer is clipped out of view, and the app crashes with the following error:
*** Assertion failure in -[NSISLinearExpression addVariable:coefficient:], /Library/Caches/com.apple.xbs/Sources/Foundation/Foundation-1349.91/Layout.subproj/IncrementalSimplex/NSISLinearExpression.m:716
One problem might be that I am setting the insets according to UIScrollView frame size, because the contained NSTextView's coordinates don't seem to be relative but absolute after magnification.
Is there any safe way to magnifying this sort of view and keeping it centered to its content at all times? And why are my constraints breaking?

I've run into similar problems, and unfortunately I ended up doing the centering myself. Here are some of the highlights of my solution.
needs recursion prevention! (otherwise stackoverflow :)
create a non-drawable NSView as the documentView, and then add your drawable view as a subview which is centered manually, and manually set the frame to the visibleRect of the parent.
override visibleRect, call it a second time if its invalid, and debug to make sure it is valid!
zooming layered backed views sux. You could try using an NSTiledLayer, but I've tried and abandoned that solution multiple times.
Code below:
#interface FlippedParentView : NSView
#end
#implementation FlippedParentView
- (BOOL) isFlipped { return YES; }
#end
- (void)awakeFromNib
{
[self resetMouseInfo];
[[self window] setAcceptsMouseMovedEvents:YES];
needsFullRedraw = YES;
[self setAcceptsTouchEvents:YES];
// problem: when zoomed-in, CALayer backed NSOpenGLView becomes too large
// and hurts performance.
// solution: create a fullsizeView for the NSScrollView to resize,
// and make NSOpenGLView a subview. Keep NSOpenGLView size the same as visibleRect,
// positioning it as needed on the fullsizeView.
NSScrollView *scrollvw = [self enclosingScrollView];
[scrollvw setBackgroundColor:[NSColor darkStrokeColor]];
fullsizeView = [[FlippedParentView alloc] initWithFrame: [self frame]];
[scrollvw setDocumentView:fullsizeView];
[fullsizeView setAutoresizesSubviews:NO];
//printf("mask %d\n", [self autoresizingMask]);
[fullsizeView setAutoresizingMask: NSViewHeightSizable | NSViewWidthSizable | NSViewMinYMargin | NSViewMaxYMargin | NSViewMaxXMargin | NSViewMinXMargin];
[self setAutoresizingMask: NSViewNotSizable];
[fullsizeView addSubview:self];
}
- (NSRect) visibleRect
{
NSRect visRect = [super visibleRect];
if ( visRect.size.width == 0 )
{
visRect = [[self superview] visibleRect];
if ( visRect.size.width == 0 )
{
// this jacks up everything
DUMP( #"bad visibleRect" );
}
visRect.origin = NSZeroPoint;
}
return visRect;
}
- (void) _my_zoom: (double)newZoom
{
mouseFocusPt = [self focusPt];
NSRect oldVisRect = [[self superview] visibleRect];
if ( newZoom < 1.0 )
newZoom = 1.0;
if ( newZoom > kZoomFactorMax ) newZoom = kZoomFactorMax;
float xpct = (mouseFocusPt.x - oldVisRect.origin.x) /
( NSMaxX(oldVisRect) - oldVisRect.origin.x );
float ypct = (mouseFocusPt.y - oldVisRect.origin.y) /
( NSMaxY(oldVisRect) - oldVisRect.origin.y );
float oldZoom = zoomFactor;
zoomFactor = newZoom;
/////////////////////////////////////////////////////////////////////////////////////////////////////
// Stay locked on users' relative mouse location, so user can zoom in and back out without
// the view scrolling out from under the mouse location.
NSPoint newFocusPt = NSMakePoint (mouseFocusPt.x * newZoom/oldZoom,
mouseFocusPt.y * newZoom/oldZoom) ;
NSRect myFrame = fullsizeFrame; // [self frame];
float marginPercent = (myFrame.size.height - drawableSizeWithMargins.height) / drawableSizeWithMargins.height;
[self updateContext];
NSRect newVisRect;
newVisRect.size = [self visibleRect].size;
newVisRect.origin.x = (newFocusPt.x) - (xpct * newVisRect.size.width);
//DLog( #"xpct %0.2f, zoomFactor %0.2f, newVisRect.origin.x %0.2f", xpct, zoomFactor, newVisRect.origin.x);
myFrame = fullsizeFrame; // [self frame];
float marginPercent2 = (myFrame.size.height - drawableSizeWithMargins.height) / drawableSizeWithMargins.height;
float marginDiff = (marginPercent - marginPercent2) * drawableSizeWithMargins.height;
newVisRect.origin.y = (newFocusPt.y ) - (ypct * newVisRect.size.height) - marginDiff;
//DLog( #"ypct %0.2f, zoomFactor %0.2f, newVisRect.origin.y %0.2f", ypct, zoomFactor, newVisRect.origin.y);
//DLog( #"marginPercent %0.2f newVisRect %#", marginPercent, NSStringFromRect(newVisRect) );
if ( newVisRect.origin.x < 1 ) newVisRect.origin.x = 1;
if ( newVisRect.origin.y < 1 ) newVisRect.origin.y = 1;
// NSLog( #"zoom scrollRectToVisible %# bounds %#", NSStringFromRect(newVisRect), NSStringFromRect([[self superview] bounds]) );
// if ( iUseMousePt || isSlider )
[[self superview] scrollRectToVisible:newVisRect];
}
// - zoomFactor of 1.0 is defined as the zoomFactor needed to show entire selected context within visibleRect,
// including margins of 5% of the context size
// - zoomFactor > 1.0 will make pixels look bigger (view a subsection of a larger total drawableSize)
// - zoomFactor < 1.0 will make pixels look smaller (selectedContext size will be less than drawableSize)
-(void)updateContext
{
static BOOL sRecursing = NO;
if ( sRecursing ) return; // prevent recursion
sRecursing = YES;
//NSRect scrollRect = [[self superview] frame];
NSRect clipViewRect = [[[self enclosingScrollView] contentView] frame];
NSRect visRect = [[self superview] visibleRect]; // careful... visibleRect is sometimes NSZeroRect
float layoutWidth = clipViewRect.size.width;
float layoutHeight = clipViewRect.size.height;
marginPct = layoutHeight / (layoutHeight - (overlayViewMargin*2) );
// Satisfy the constraints fully-zoomed-out case:
// 1) the drawable rect is centered in the view with at margins.
// Allow for 5% margins (1.025 = 2.5% left, right, top, bottom)
// 2) guarantee the drawable rect does not overlap the mini-map in upper right corner.
NSRect baseRect = NSZeroRect;
baseRect.size = visRect.size;
NSRect drawableBaseRect = getCenteredRectFloat(baseRect, metaUnionRect.size );
//drawableSizeWithMargins = nsIntegralSize( nsScaleSize( drawableBaseRect.size, zoomFactor ) );
drawableSizeWithMargins = nsScaleSize( drawableBaseRect.size, zoomFactor );
// drawableSize will NOT include the margins. We loop until we've satisfied
// the constraints above.
drawableSize = drawableSizeWithMargins;
do
{
NSSize shrunkSize;
shrunkSize.width = layoutWidth / marginPct;
shrunkSize.height = layoutHeight / marginPct;
//drawableSize = nsIntegralSize( nsScaleSize( drawableBaseRect.size, zoomFactor / marginPct ));
drawableSize = nsScaleSize( drawableBaseRect.size, zoomFactor / marginPct );
[self calculateMiniMapRect]; // get approx. size. Will calculate once more below.
NSRect shrunkRect = getCenteredRectNoScaling(baseRect, shrunkSize );
// DLog( #"rough miniMapRect %# shrunk %#", NSStringFromRect(miniMapRect), NSStringFromRect(shrunkRect));
// make sure minimap doesn't overlap drawable when you scroll to top-left
NSRect topMiniMapRect = miniMapRect;
topMiniMapRect.origin.x -= visRect.origin.x;
topMiniMapRect.origin.y = 0;
if ( !NSIntersectsRect( topMiniMapRect, shrunkRect ) )
{
topMarginPercent = fabs(shrunkRect.origin.y - drawableBaseRect.origin.y) / baseRect.size.height;
break;
}
float topMarginOffset = shrunkRect.size.height + (baseRect.size.height * 0.025);
shrunkRect.origin.y = NSMaxY(baseRect) - topMarginOffset;
if ( !NSIntersectsRect( topMiniMapRect, shrunkRect ) )
{
topMarginPercent = fabs(shrunkRect.origin.y - drawableBaseRect.origin.y) / baseRect.size.height;
break;
}
marginPct *= 1.025;
} while (1);
fullsizeFrame.origin = NSZeroPoint;
fullsizeFrame.size.width = fmax(drawableSizeWithMargins.width, layoutWidth);
fullsizeFrame.size.height = fmax(drawableSizeWithMargins.height, layoutHeight);
[fullsizeView setFrame:fullsizeFrame];
NSRect myNewFrame = [fullsizeView visibleRect];
if (myNewFrame.size.width > 0)
[self setFrame: myNewFrame]; //NSView
sRecursing = NO;
}

Related

UIScrollVIew with UIImageView, zoomed image sitck to right edge

I have made UIScrollView with three UIScrollViews inside and UImageView inside those three scollviews (Like in this answer: https://stackoverflow.com/a/25768875/2911156). Almost everything works, but zooming in landscape mode is broken. When user zoom in image, after zooming is finished, image is moved to the right edge and cut. How to center image after zoom, or what is wrong here?
UI Structure:
Main Scroll view Constraints:
Image View constraint (all three had same pattern)
I'm not doing much in code (ScrollViews and ImageViews are based on autolayout), just done this:
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
return _photoImage;
}
and this:
- (void) setMaxMinZoomScaleForBonds {
self.contentInset = UIEdgeInsetsZero; //need to reset it before load image, because previous insets can be wrong
self.minimumZoomScale = 1.0;
self.zoomScale = 1.0;
self.maximumZoomScale = 2*[UIScreen mainScreen].scale;
}
One more thing done, but it does not work:
- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(CGFloat)scale {
//center image when zooming.
CGFloat x = 0;
CGFloat y = 0;
CGFloat screenWidth = [[UIScreen mainScreen] bounds].size.width;
CGFloat screenHight = [[UIScreen mainScreen] bounds].size.height;
CGFloat viewWidth = view.frame.size.width;
CGFloat viewHight = view.frame.size.height;
if(viewWidth < screenWidth)
x = screenWidth/ 2;
if(viewHight < screenHight)
y = screenHight / 2 ;
if(scale<=1.1)
self.contentInset = UIEdgeInsetsZero;
else
self.contentInset = UIEdgeInsetsMake(y, x, y, x);
}
That is all related to zoom action.

Cocos2d: Testing for collisions, says that the two sprites always intersect

Hello I am making a cocos2d side scroller. I am trying to test for collisions between two sprites. I checked and the rects I am making for the sprites are what they are supposed to be, but it says that the two rects intersect all of the time whether or not they actually do. Here is the code:
-(void)checkForRedEnemyCollisions{
CGRect playerRect = CGRectMake(player.position.x - (player.playerSprite.contentSize.width/2),
player.position.y - (player.playerSprite.contentSize.height/2),
player.playerSprite.contentSize.width,
player.playerSprite.contentSize.height);
CGRect redEnemyRect = CGRectMake(redEnemy.position.x - (redEnemy.bulletSprite.contentSize.width / 2) ,
redEnemy.position.y - (redEnemy.bulletSprite.contentSize.height /2 ),
redEnemy.bulletSprite.contentSize.width,
redEnemy.bulletSprite.contentSize.height);
if (CGRectIntersectsRect(playerRect, redEnemyRect)) {
CCLOG(#"collision");
}
}
Here is more code:
-(id)init{
if((self = [super init])){
CGSize size = [[CCDirector sharedDirector]winSize];
screenWidth = size.width;
screenHeight = size.height;
gravity = 2;
playerSprite = [CCSprite spriteWithFile:#"thefinalcharacter.png"];
playerSprite.scale = 1.5;
playerSprite.position = ccp(screenWidth/3.4, screenHeight/2);
[self addChild:playerSprite z:-3];
[self schedule: #selector(flight:)interval:1.0f/7.0f];
}
return self;
}
-(void)flight:(ccTime)delta{
flightCounter ++;
if (flightCounter % 2){
[playerSprite setTexture:[[CCSprite spriteWithFile:#"thefinalcharacter.png"]texture]];
}else{
[playerSprite setTexture:[[CCSprite spriteWithFile:#"thefinalcharacter2.png"]texture]];
}
[self schedule:#selector(updatePosition:)interval:1.0f/30.0f];
}
-(void)updatePosition:(ccTime)delta{
if(playerSprite.position.y < 35){
gravity = 0;
}else if(playerSprite.position.y > screenHeight - 150) {
playerSprite.position = ccp(playerSprite.position.x, playerSprite.position.y - 100);
}else{
gravity = 2;
}
playerSprite.position = ccp(playerSprite.position.x, playerSprite.position.y - gravity);
}
make sure that you are creating the bounding rectangles in world coordinates.
try to convert the points to world coordinates like the following;
CGPoint playerWorldPosition = [player ConvertToWorldSpace:player.position];
CGRect playerRect = CGRectMake(playerWorldPosition.x - (player.playerSprite.contentSize.width/2),
playerWorldPosition.y - (player.playerSprite.contentSize.height/2),
player.playerSprite.contentSize.width,
player.playerSprite.contentSize.height);
do the same for redEnemy. this will make sure you are comparing them in world space but not in another space down the hierarchy of layers.
if you using anchor point instead of the default (0.0,0.0) on your player or redEnemies. use the ConvertToWorldSpaceAR function instead of the ConvertToWorldSpace.

NSWindow doesn't respond until click

I've got an image editor window. After drag event I minimize my window to miniature in the bottom left corner, after drop on the miniature I return window back. Above the miniature I change cursor to operationNotAllowedCursor.
The problem is: NSWindow does not change the cursor on miniature after the first draggingEntered (after the second and more everything's fine). Moreover, after drop on the miniature NSWindow does not receive any events until a click on any area of it.
Code for minimizing window (in NSWindow subclass):
-(void)minimize
{
const double miniSize = 240;
MSDraggingMiniature *mini = [[MSDraggingMiniature alloc] init];
[mini setMiniImage:[[MSScreenMaker getInstance] makeEditorScreen:(int)[self windowNumber]]];
_mainContentView = self.contentView;
_oldFrame = [self frame];
[self setStyleMask:NSBorderlessWindowMask];
self.contentView = mini;
NSRect drect = [[self screen] frame];
double width, height;
if (self.frame.size.width < self.frame.size.height) {
height = miniSize;
width = self.frame.size.width / self.frame.size.height * miniSize;
} else {
width = miniSize;
height = self.frame.size.height / self.frame.size.width * miniSize;
}
_anima = MSEditorResizeAnimationMinimize;
[self setFrame:NSMakeRect(drect.origin.x + 20, drect.origin.y + 20 , width, height) display:YES animate:YES];
}
-(void)deminimize
{
self.contentView = _mainContentView;
[self setStyleMask:NSTitledWindowMask];
_anima = MSEditorResizeAnimationDeminimize;
[self setFrame:_oldFrame display:YES animate:YES];
[self makeKeyWindow];
[self makeMainWindow];
}
After lots of variants I found the answer. It seems that area, acting like Dragging Source, must be just NSView, not a subclass of NSButton, which I had.
I rewrote my Dragging Source class - now everything works fine.

Autoscroll UIScrollView when subview is out of the visible area

EDIT:
I uploaded my implementation to github. Maybe it is better to understand what I want and what my problem is.
I changed the code in the github project a little bit than the posted code here. I think the implementation in the github project is better, but not perfect.
What I want to do:
Have a UIScrollView with movable UIViews (e.g. Images). The user can pan this subviews and zoom in and out. When the user zooms in and moves a subview over the current visible area the scrollView should automatically scroll. As lang as the subview is over the edge the scrollview should scroll. When the subview isn't over the visible area anymore the scrollview should stop moving.
I try to explain my problem as good as possible.
What I have managed to do:
Zoom the scrollview, move the subviews with the UIPanGestureRecognizer, recognize when the subview is over the visible area and start moving (changing the contentOffset) the scrollview. Here I using a NSTimer to move the scrollview as long as the subview is over the visible area.
My problem:
When the subview is over the visible area a NSTimer is started, to change the contentOffset of the subview and the frame (position) of the subview.
After that I can't pan the subview anymore.
I can't figure out how to implement the pan gesture with changing the subview frame in a correct way.
My implementation:
I am using three views:
UIScrollView
MyImageContainerView (UIView, added as a subview to the scrollview)
MyImageView (UIView, added as a subview to MyImageContainerView)
Currently MyImageContainerView manages the workflow. A MyImageView has a UIPanGestureRecognizer attached. The method for this recognizer is implemented in MyImageContainerView:
- (void)handlePanGesture:(UIPanGestureRecognizer *)gestureRecognizer
{
//UIView which is moved by the user
MyImageView *currentView = gestureRecognizer.view;
switch (gestureRecognizer.state)
{
case UIGestureRecognizerStatePossible:
{
break;
}
case UIGestureRecognizerStateBegan:
{
//save both values in global instance variables
currentFrameOriginX = currentView.frame.origin.x;
currentFrameOriginY = currentView.frame.origin.y;
//global BOOL variable, to check if scrollView movement is performed
scrolling = NO;
break;
}
case UIGestureRecognizerStateChanged:
{
CGRect rect = CGRectMake(currentFrameOriginX + [gestureRecognizer translationInView:currentView.superview].x, currentFrameOriginY + [gestureRecognizer translationInView:currentView.superview].y, currentView.frame.size.width, currentView.frame.size.height);
if (CGRectContainsRect(currentView.superview.frame, rect)) {
/*PROBLEM: Here is a problem. I need this change of the frame here, to move the UIView along the movement from the user. In my autoScroll-method I have to set the frame of currentView, too. But I can't set the frame of currentView here and in the autoScroll. But as long as the NSTimer runs and is calling autoScroll: this if-statement isn't called, so I can't move the UIView with my finger anymore. */
if (!scrolling) {
//currently the NSTimer method for the automatically scrolling isn't performed, so:
//change the frame according to the pan gesture
currentView.frame = rect;
}
UIScrollView *scrollView = self.myScrollView; //reference to the "root" UIScrollView
CGRect visibleRect;
visibleRect.origin = scrollView.contentOffset;
visibleRect.size = scrollView.bounds.size;
CGRect frame = currentView.frame;
CGFloat scale = 1.0 / scrollView.zoomScale;
visibleRect.origin.x *= scale;
visibleRect.origin.y *= scale;
visibleRect.size.width *= scale;
visibleRect.size.height *= scale;
CGSize scrollZone = CGSizeMake(10.0f, 10.0f);
float scrollStep = 3.0f;
CGPoint scrollAmount = CGPointZero;
//determine the change of x and y
if (frame.origin.x+scrollZone.width < visibleRect.origin.x) {
scrollAmount.x = -scrollStep;
}
else if((frame.origin.x+frame.size.width)-scrollZone.width > visibleRect.origin.x + visibleRect.size.width) {
scrollAmount.x = scrollStep;
}
else if (frame.origin.y+scrollZone.height < visibleRect.origin.y) {
scrollAmount.y = -scrollStep;
}
else if((frame.origin.y+frame.size.height)-scrollZone.height > visibleRect.origin.y + visibleRect.size.height) {
scrollAmount.y = scrollStep;
}
if ((scrollAmount.x != 0) | (scrollAmount.y != 0)) {
if (![scrollTimer isValid]) {
//scrollTimer is a global NSTimer instance variable
[scrollTimer invalidate];
scrollTimer = nil;
NSString *scrollString = NSStringFromCGPoint(scrollAmount);
NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys:scrollString, #"scrollString", currentView, #"currentView", nil];
scrollTimer = [[NSTimer alloc]initWithFireDate:[NSDate date] interval:0.03f target:self selector:#selector(autoScroll:) userInfo:info repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:scrollTimer forMode:NSRunLoopCommonModes];
}
}
else {
[scrollTimer invalidate];
scrollTimer = nil;
scrolling = NO;
}
}
break;
}
case UIGestureRecognizerStateEnded:
{
//quite know the scrolling should stop, maybe it would be better when the scrollView scrolls even if the user does nothing when the subview is over the visible area
[scrollTimer invalidate];
scrollTimer = nil;
scrolling = NO;
break;
}
default:
{
[scrollTimer invalidate];
scrollTimer = nil;
scrolling = NO;
break;
}
}
}
-(void)autoScroll:(NSTimer*)timer {
scrolling = YES; //the scroll method is executed quite know
NSDictionary *info = [timer userInfo];
UIScrollView *scrollView = self.myScrollView;
CGRect visibleRect;
visibleRect.origin = scrollView.contentOffset;
visibleRect.size = scrollView.bounds.size;
CGPoint scrollAmount = CGPointFromString([info objectForKey:#"scrollString"]);
MyImageView *currentView = [info objectForKey:#"currentView"];
//stop scrolling when the UIView is at the edge of the containerView (referenced over 'self')
if ((currentView.frame.origin.x <= 0 | currentView.frame.origin.y <= 0) ||
((currentView.frame.origin.x+currentView.frame.size.width) > self.frame.size.width | (currentView.frame.origin.y+currentView.frame.size.height) > self.frame.size.height)
) {
scrolling = NO;
return;
}
//move the UIView
CGFloat scale = 1.0 / scrollView.zoomScale;
if (scrollAmount.x != 0) {
scrollAmount.x *= scale;
}
if (scrollAmount.y != 0) {
scrollAmount.y *= scale;
}
CGRect frame = currentView.frame;
frame.origin.x += scrollAmount.x;
frame.origin.y += scrollAmount.y;
currentView.frame = frame;
currentFrameOriginX = currentView.frame.origin.x;
currentFrameOriginY = currentView.frame.origin.y;
//move the scrollView
CGPoint contentOffset = scrollView.contentOffset;
contentOffset.x += scrollAmount.x;
contentOffset.y += scrollAmount.y;
[scrollView setContentOffset:contentOffset animated:NO];
}

UIView hitTest:withEvent: and pointInside:withEvent

I'm working with Mixare AR SDK for iOS and I need to solve some bugs that happends, one of them is show the information of a POI when the POI's view is tapped.
Prelude:
Mixare has an overlay UIView within MarkerView views are placed, MarkerView views are moving around the screen to geolocate the POIs and each one has two subviews, an UIImageView and an UILabel.
Issue:
Now, for example, there are 3 visible POIs in the screen, so there are 3 MarkerView as overlay subviews. If you touch anywhere in the overlay, a info view associated to a random POI of which are visible is showed.
Desired:
I want that the associated POI's info is shown only when the user tapped a MarkerView
Let's work. I've see that MarkerView inherits from UIView and implements hitTest:withEvent
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
viewTouched = (MarkerView*)[super hitTest:point withEvent:event];
return self;
}
I've put a breakpoint and hitTest is called once for each visible MarkerView but loadedView always is null so I can't work with it, so I've tried to check if the hit point is inside the MarkerView frame implementing pointInside:withEvent: by this way
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
NSLog(#"ClassName: %#", [[self class] description]);
NSLog(#"Point Inside: %f, %f", point.x, point.y);
NSLog(#"Frame x: %f y: %f widht:%f height:%f", self.frame.origin.x, self.frame.origin.y, self.frame.size.width, self.frame.size.height);
if (CGRectContainsPoint(self.frame, point))
return YES;
else
return NO;
return YES;
}
But this function always returns NO, even when I touch the MarkerView. When I check the log I saw that X and Y point values has negative values sometimes and width and height of the view are very small, 0.00022 or similar instead of 100 x 150 that I set the MarkerView frame on its initialization.
Here you are a extract of my log in which you can see the class name, the point and the MarkerView frame values.
ClassName: MarkerView
2011-12-29 13:20:32.679 paisromanico[2996:707] Point Inside: 105.224899, 49.049023
2011-12-29 13:20:32.683 paisromanico[2996:707] Frame x: 187.568573 y: 245.735138 widht:0.021862 height:0.016427
I'm very lost with this issue so any help will be welcome. Thanks in advance for any help provided and I'm sorry about this brick :(
Edit:
At last I've found that the problem is not in hitTest:withEvent: or pointInside:withEvent, problem is with CGTransform that applies to the MarkerView for scaling based on distande and rotating the view, if I comment any code related to this, the Mixare AR SDK works fine, I mean, info view is shown correctly if you touch a marker and doesn't do anything if any other place in the screen is touched.
So, by the moment, I've not solved the problem but I applied a patch removing the CGTransform related code in AugmentedViewController.m class - (void)updateLocations:(NSTimer *)timer function
- (void)updateLocations:(NSTimer *)timer {
//update locations!
if (!ar_coordinateViews || ar_coordinateViews.count == 0) {
return;
}
int index = 0;
NSMutableArray * radarPointValues= [[NSMutableArray alloc]initWithCapacity:[ar_coordinates count]];
for (PoiItem *item in ar_coordinates) {
MarkerView *viewToDraw = [ar_coordinateViews objectAtIndex:index];
viewToDraw.tag = index;
if ([self viewportContainsCoordinate:item]) {
CGPoint loc = [self pointInView:ar_overlayView forCoordinate:item];
CGFloat scaleFactor = 1.5;
if (self.scaleViewsBasedOnDistance) {
scaleFactor = 1.0 - self.minimumScaleFactor * (item.radialDistance / self.maximumScaleDistance);
}
float width = viewToDraw.bounds.size.width ;//* scaleFactor;
float height = viewToDraw.bounds.size.height; // * scaleFactor;
viewToDraw.frame = CGRectMake(loc.x - width / 2.0, loc.y-height / 2.0, width, height);
/*
CATransform3D transform = CATransform3DIdentity;
//set the scale if it needs it.
if (self.scaleViewsBasedOnDistance) {
//scale the perspective transform if we have one.
transform = CATransform3DScale(transform, scaleFactor, scaleFactor, scaleFactor);
}
if (self.rotateViewsBasedOnPerspective) {
transform.m34 = 1.0 / 300.0;
double itemAzimuth = item.azimuth;
double centerAzimuth = self.centerCoordinate.azimuth;
if (itemAzimuth - centerAzimuth > M_PI) centerAzimuth += 2*M_PI;
if (itemAzimuth - centerAzimuth < -M_PI) itemAzimuth += 2*M_PI;
double angleDifference = itemAzimuth - centerAzimuth;
transform = CATransform3DRotate(transform, self.maximumRotationAngle * angleDifference / (VIEWPORT_HEIGHT_RADIANS / 2.0) , 0, 1, 0);
}
viewToDraw.layer.transform = transform;
*/
//if we don't have a superview, set it up.
if (!(viewToDraw.superview)) {
[ar_overlayView addSubview:viewToDraw];
[ar_overlayView sendSubviewToBack:viewToDraw];
}
} else {
[viewToDraw removeFromSuperview];
viewToDraw.transform = CGAffineTransformIdentity;
}
[radarPointValues addObject:item];
index++;
}
float radius = [[[NSUserDefaults standardUserDefaults] objectForKey:#"radius"] floatValue];
if(radius <= 0 || radius > 100){
radius = 5.0;
}
radarView.pois = radarPointValues;
radarView.radius = radius;
[radarView setNeedsDisplay];
[radarPointValues release];
}
Any CoreGrapics or UI expert could give us his point of view about this issue??
You should either try to hittest as attached:
if ([self pointInside:point withEvent:event]) {
// do something
}
I would suggest you add the hit test on the superview, and do the following in the hit test of the parent of the markerViews
if ([markerView pointInside:point withEvent:event]) {
// extract the tag and show the relevant info
}
Hope this helps