I have a TableView with custom TableCellViews that has UILabels and UIButtons on it.
when one of the buttons is taped I want to show a "tooltip" describing the text of the button.
Most everything is working except for when I try to convert the center coordinates of the UIButton to the coordinates of the rootView which is a UIView.
Here is the code:
- (void) fancyLabelButtonPressed: (UIButton *) button {
CGPoint btnPoint = button.center; // x=200.5 y=27.5
CGPoint rootViewPoint = [button convertPoint:btnPoint toView:rootView];
// rootViewPoint -> x=390.5 y=197.5
CGPoint pointToUse = CGPointMake(btnPoint.x +20, rootViewPoint.y - 23); // Hack to get it close
}
How can rootViewPoint.x=390.5 when I'm in portrait view?!!? By using the x from the button and the y from the rootViewPoint I get close to what it should be but it is just a hack.
Does anyone see what I'm doing wrong? or is there a better way?
This is because you are converting the point from the wrong view. The center property is actually in the coordinate system of the button's superview, whatever that is, so when you convert it to your rootView, you must convert it from there. So:
rootViewPoint = [[button superview] convertPoint:btnPoint toView:rootView];
That should give you what you are looking for.
Related
I'm trying to detect a touch on a UISubview of a view being animated
Here's my detection code:
//simple but not very elegant way of getting correct frame
CGRect keepFrame = self.fishContainer.layer.frame;
self.fishContainer.layer.frame = [[self.fishContainer.layer presentationLayer] frame];
//get touch location, taking presentation layer into account. (See above)
CGPoint p = [sender locationInView:self.fishContainer];
CALayer *layer =[self.fishContainer.layer presentationLayer];
//apply relevant transform
p = CGPointApplyAffineTransform(p,layer.affineTransform);
EBLog(#"checking point %#",NSStringFromCGPoint(p));
UIView *vToRemove = nil;
//find topmost view containing touched point
for (UIView *v in self.parasites) {
EBLog(#"-BOUND %#",NSStringFromCGRect(v.frame));
if(CGRectContainsPoint(v.frame, p))
{
vToRemove = v;
}
}
//OK, we have a view. Let's remove it.
if(vToRemove)
{
EBLog(#"found one");
[vToRemove removeFromSuperview];
[self.parasites removeObject:vToRemove];
if ([self.parasites count] == 0) {
[self showWinnerScreen];
[self stopGame];
}
}
//restore view frame
self.fishContainer.layer.frame = keepFrame;
Everything works correctly as long as I don't animate parasiteArea parentview.
When I animate parasiteArea's parentview (A CAAnimation consisting of move of the view, scale of the view, and rotate of the view) , the touch is outside the bounds of the expected subview.
UPDATE
I manged to get the detection working in most cases (see code above), by using the presentationLayer property and CGPointApplyAffineTransform. There is however, still some cases where it dosnt work.
I guess I need to translate the touch point to the coordinate space of the CAAnimation.
Or something like that? any suggestions?
I ended up using UIView animateWithDuration instead of CAAninmation. For my purpose the limited animation possibles were enough.
We had some consultants build some code for us and the way they have it laid out is there is a UIScrollView, and a UIView inside it.
There is another UIView icon that gets added as a subview to the scrollView based on a selection in a table. When this icon gets selected a popover is presented. In the present popover code, they use the gesture's view to calculate the frame and where to present the popover.
Now I'm tasked to present the popover with a selection in the table. When the zoom is = 1, that's pretty easy. I just grab the size of the UIScrollView, calculate where I add the icon, then present the view from that icon.
When I do it at a different zoom level though, my code falls apart since I don't know exactly where I am in the scrollView. I log the consultant's gesture from their method and they'll get origin.y position's of 5000. That's why I'm asking on how the locationInView works inside a scrollView to try to figure out where I am in the yPosition to present my popover from the right place. Any thoughts? Thanks.
- (void)handleSingleTap:(UIGestureRecognizer *)sender {
id targetView = [sender view];
CGPoint point = [sender locationInView:self.scrollView];
NSLog(#"point: %f %f", point.x, point.y);
I have a uiview at the top of the interface (below the status bar) that only the bottom part of it is shown.
Actually, I want to make the red uiview to slide down to be entirely shown by drag such as the notificationcenter in the native iOS and not just by taping a button.
What should I use to "touch and pull down" the uiview so it could be shown entirely ?
No needs to find a workaround of drag-n-drop. An UIScrollView can do it without any performance loss brought by listening on touches.
#interface PulldownView : UIScrollView
#end
#implementation PulldownView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (!self) {
return self;
}
self.pagingEnabled = YES;
self.bounces = NO;
self.showsVerticalScrollIndicator = NO;
[self setBackgroundColor:[UIColor clearColor]];
double pixelsOutside = 20;// How many pixels left outside.
self.contentSize = CGSizeMake(320, frame.size.height * 2 - pixelsOutside);
// redArea is the draggable area in red.
UIView *redArea = [[UIView alloc] initWithFrame:frame];
redArea.backgroundColor = [UIColor redColor];
[self addSubview:redArea];
return self;
}
// What this method does is to make sure that the user can only drag the view from inside the area in red.
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
if (point.y > height)
{
// Leaving useless touches to the views under it.
return nil;
}
return [super hitTest:point withEvent:event];
}
#end
How to use:
1. Initialize an instance of PulldownView.
2. Add any content you want to display to the instance using [addSubview:].
3. Hide the area in red.
[pulldownView setContentOffset:CGPointMake(0, heightOfTheView - pixelsOutside)];
This is a simple example. You can add any features to it like adding a titled button bar on the bottom of the draggable area to implement click-n-drop, or adding some method to the interface to reposition it by the caller.
Make a subclass of UIView.
Override touchesBegan:withEvent and touchesMoved:withEvent.
In the touchesBegan perhaps make a visual change so the user knows they are touching the view.
In the touchesMoved use
[[touches anyObject] locationInView:self]
and
[[touches anyObject] previousLocationInView:self]
to calculate the difference between the current touch position and the last touch position (detect drag down or drag back up).
Then if you're custom drawing, call [self setNeedsDisplay] to tell your view to redraw in it's drawRect:(CGRect)rect method.
Note: this assumes multiple touch is not used by this view.
Refer to my answer in iPhone App: implementation of Drag and drop images in UIView
You just need to use TouchesBegin and TouchesEnded methods. In that example, I have shown how to use CGPoint, Instead of that you have to try to use setFrame or drawRect for your view.
As soon as TouchesMoved method is called you have to use setFrame or drawRect (not sure but which ever works, mostly setFrame) also take the height from CGPoint.
Basically I have created a UISwipeGestureRecognizer for my UITableView for when the user swipes from left to right. When the user swipes right to left, the DELETE renders and functions as it should.
I'm trying to get a new custom UIButton to render exactly the same way the DELETE button renders when swiped. It doesn't slide in but sort of slowly reveals itself from right to left?
I would like to do this for my custom UIButton to appear in the same place as DELETE except this time only when the user swipes right to left.
I have this method set as the #selector after a swipe has been detected right to left:
- (void)displayAddArchiveView:(UIGestureRecognizer *)gestureRecognizer {
// Assume this is the view container that will contain my UIButton
GreenGradientView *archiveView = [[GreenGradientView alloc] initWithFrame:CGRectMake(211.0, 3.0, 63.0, 30.0)];
if (gestureRecognizer.state == UIGestureRecognizerStateEnded) {
CGPoint swipeLocation = [gestureRecognizer locationInView:self.reportsTableView];
NSIndexPath *swipedIndexPath = [self.reportsTableView indexPathForRowAtPoint:swipeLocation];
ReportsTableViewCell *swipedCell = (ReportsTableViewCell *)[self.reportsTableView cellForRowAtIndexPath:swipedIndexPath];
[self tableView:reportsTableView willBeginEditingRowAtIndexPath:swipedIndexPath];
// HERE I should do some sort of UIView animation block to slowly reveal it
// from the right to left or something? Any suggestions. Not sure how to go about it?
[swipedCell addSubview:archiveView];
}
}
I have indicated by comments inside the code block my thoughts. The custom UIView actually does show up after swiping so I know it works. It's just a matter of getting the animation right, identical to how the DELETE button reveals itself? Suggestions?
Thanks!
You could try something like this
CGRect finalFrame = archiveView.frame;
archiveView.frame = CGRectMake(finalFrame.origin.x + finalFrame.size.width, finalFrame.origin.y, 0, finalFrame.size.height);
[UIView animateWithDuration:0.3 animations:^{
archiveView.frame = finalFrame;
}];
Basically I've reset the frame so it has no width and set the origin on the far right (so that it grows from the right towards the left). Then in the animation block, i set the frame to what I want it to be at the end. In your case you can just save the final frame in a local variable before you reset the frame to have zero width.
It is ok to set the max & min value for a UISlider control in iPhone. Accordingly slider will work - OK - Good.
First, I will give an example of what exactly I want to implement.
In iPad comes with Calendar application as a default.
Open calendar, at the bottom - you can see day/week/month listing
When you slide your finger on it, you will see a pop up menu when sliding.
I want to implement the same functionality on slider.
I am trying hard, But if I am able to get x co-ordinate, I can easily implement it. Any other suggestion for implementing same are most most welcome.
Start out with a UILabel for testing (just write something in it) and add IBOulets to it, as well as the UISlider. Next, add a IBAction for "value changed" for the slider.
You then need to calculate two values to figure out where your label should be placed -- adjustment from left, and pixels per value for the slider. Your #interface might look like this:
#interface sliderViewController : UIViewController {
IBOutlet UISlider *slider;
IBOutlet UILabel *label;
float pixelsPerValue;
float leftAdjust;
}
-(IBAction) sliderValueChanged:(id)sender;
#end
In e.g. viewDidLoad: for your view controller, you do the calculation for the two floats.
- (void)viewDidLoad {
float width = slider.frame.size.width;
pixelsPerValue = width / (slider.maximumValue - slider.minimumValue);
leftAdjust = slider.frame.origin.x;
[super viewDidLoad];
}
And finally, your IBAction might look like this:
-(IBAction) sliderValueChanged:(id)sender
{
NSLog(#"changed");
CGRect frame = label.frame;
frame.origin.x = leftAdjust + (pixelsPerValue * slider.value);
label.frame = frame;
}
Hope that helps.
I don't think that thing is implemented with a UISlider, but you can use the x-coordinate of the touch easily.
In the UISlider's action, you could accept a 2nd argument.
-(void)action:(UISlider*)sender forEvent:(UIEvent*)event
// ^^^^^^^^^^^^^^^^^^^^^^^^
From the UIEvent you can get a UITouch which contains its x, y coordinates.