Right now i am working on custom control for fitness related app following is the image of control i have developed so far.
the red view can be draggable vertically only from inner most circle to outer circle.
i have so far managed to drag view vertically from inner most circle to outer circle.
here what i want to achieve,
each circle represent 2500 steps for e.g. innermost circle is 0,then 2500,5000 till outer circle 15000
user can select goal in increment of 500 only
so i am stuck at how to convert red view's sliding points to steps
also i have noticed when i swipe fast i got irregular interval of current touch points so steps increment are low on fast swipe & high on low speed swipe.
any help pointing towards solution is highly appreciated.
here is my code so far.
Circle Code
I would use an overlay UIScrollView for the red draggable part. And only allow vertical scrolling and set pagingEnabled to NO. And set its delegate as the viewcontroller.
Then, you can make your custom paging for 500step per page as follows by setting kCellHeigth to the number of pixels corresponding to 500steps in your image:
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
{
CGFloat kMaxIndex = 30; //=15000/500
CGFloat targetY = scrollView.contentOffset.y + velocity.y * 60.0;
CGFloat targetIndex = round(targetY / (kCellHeigth + kCellSpacing));
if (targetIndex < 0)
targetIndex = 0;
if (targetIndex > kMaxIndex)
targetIndex = kMaxIndex;
targetContentOffset->y = targetIndex * (kCellHeigth + kCellSpacing);
}
-- New Solution After looking at your code --
I see that for iPhone5(width=320) the radius delta between adjacent circles are roughly 20 points. And this space corresponds to 2500steps.
So 1 point = 125 steps
Since you want 500 step increments, it corresponds to 4 points in the iPhone5/5s screen. Maybe when the users touch ends, you can try to snap the navigator to the closest 4 point grid:
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"%s",__FUNCTION__);
UITouch *touch = [touches anyObject];
touchPoint = [touch locationInView:_navView];
CGPoint newCenter = CGPointMake(_navView.center.x, _navView.center.y + (touchPoint.y - touchStart.y));
CGFloat yDelta = newCenter.y - _navMinPoint.y;
int yDeltaMultiple = yDelta / 4;
int newYDelta = yDeltaMultiple * 4;
newCenter.y = newYDelta;
_navView.center = newCenter;
_labelSteps.center = [self setStepsLabelfromNavView];
}
I didn't write these in Xcode so there might be typos/errors. If this works in iPhone 5/5s, then we will need to change 4 points to a variable.
Related
I'm trying to implement drag controls with boundary limitation on UIButton.
And I wrote following code for that.
- (void)onTouchDragInside:(UIButton*)btn withEvent:(UIEvent*)event{
UITouch *touch = [[event touchesForView:btn] anyObject];
CGPoint prevPos = [touch previousLocationInView:btn];
CGPoint pos = [touch locationInView:btn];
float dX = pos.x-prevPos.x;
if (btn.frame.origin.x >= buttonOffPosition && btn.frame.origin.x <= buttonOnPosition) {
btn.center=CGPointMake(btn.center.x+dX, btn.center.y);
NSLog(#"buttonOffPos: %f", buttonOffPosition);
NSLog(#"btn.center.x+dX: %f", btn.center.x+dX);
NSLog(#"buttonOnPos: %f", buttonOnPosition);
}
}
This works almost properly. But only when the button is dragged very fast, it exceeds the limitation buttonOffPosition and buttonOnPosition.
This is the problem I want to resolve. Is there a good way to solve this problem?
Your thoughts and help will be hugely appreciated.
If you want the code to limit your button's location to between your two values you need to check what the final resting point of your button will be after this touch event is processed, not check where it currently is. If it's currently in bounds to be moved, then you move it by say 1000, it will no longer be in bounds at the end but you allow it to go there because you didn't check the end point.
You can do this several ways. The simplest one that comes to my mind is:
- (void)onTouchDragInside:(UIButton*)btn withEvent:(UIEvent*)event{
UITouch *touch = [[event touchesForView:btn] anyObject];
CGPoint prevPos = [touch previousLocationInView:btn];
CGPoint pos = [touch locationInView:btn];
float dX = pos.x-prevPos.x;
//Get the new origin after this motion
float newXOrigin = btn.frame.origin.x + dX;
//Make sure it's within your two bounds
newXOrigin = MIN(newXOrigin,buttonOnPosition);
newXOrigin = MAX(newXOrigin,buttonOffPosition);
//Now get the new dX value staying in bounds
dX = newXOrigin - btn.frame.origin.x;
btn.center=CGPointMake(btn.center.x+dX, btn.center.y);
}
This method brings up a problem with your finger no longer being inside the button as you are dragging it from one direction to the other, but I will leave that problem to your next question.
EDIT:
Here is how I would make it more readable. This is only my opinion and has no bearing on the way the code works. Outside of just modifying your code, I would create a cached starting point for the button object and do all motion events from that. That way if your button stops moving even though your finger continues, as your finger comes back the button stays with your finger. This current solution, as soon as your finger changes direction the button will start moving again even though your finger is far off the button now. But to do that would be a large code change for you.
- (void)onTouchDragInside:(UIButton*)btn withEvent:(UIEvent*)event{
//This code can go awry if there is more than one finger on the screen, careful
UITouch *touch = [[event touchesForView:btn] anyObject];
CGPoint prevPos = [touch previousLocationInView:btn];
CGPoint pos = [touch locationInView:btn];
float dX = pos.x-prevPos.x;
//Get the original position of the button
CGRect buttonFrame = btn.frame;
buttonFrame.origin.x += dX;
//Make sure it's within your two bounds
buttonFrame.origin.x = MIN(buttonFrame.origin.x,buttonOnPosition);
buttonFrame.origin.x = MAX(buttonFrame.origin.x,buttonOffPosition);
//Set the button's new frame if we need to
if(buttonFrame.origin.x != btn.frame.origin.x)
btn.frame = buttonFrame
}
I have a scrollview and I'm watching user input. I would like to know where their finger currently is on the X plane. Is this possible through the ScrollView and/or its delegate or do I have to override touchesBegan, etc?
I'm assuming you set up your scroll view delegate. If you did, then you only need to implement the scrollViewDidScroll: method from the UIScrollViewDelegate protocol...
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGPoint touchPoint = [scrollView.panGestureRecognizer locationInView:scrollView];
NSLog(#"Touch point: (%f, %f)", touchPoint.x, touchPoint.y);
}
This will update while the user is scrolling.
Additional info: Note that if you have something like a UIPageControl, that your scroll view is navigating between, you may have to calculate the x position based on the number of pages (ie if each page is 100 pixels wide, page 0 will start at point 0, page one at point 100, etc..).
CGPoint touchPoint = [scrollView.panGestureRecognizer locationInView:scrollView];
CGFloat x = touchPoint.x;
CGPoint point = [_scrollView locationOfTouch:touchIndex inView:_scrollView];
touchindex: 0 for first touch and 1, 2,3 so on
if inview:nil then point will be in the window base coordinate sysytem
CGFloat x = point.x;
CGFloat y = point.y
I use the panGesture of the scrollView because it's more accurate if you want to know the position of the x plane when you have only vertical scroll.
[self.scrollView.panGestureRecognizer addTarget:self action:#selector(handlePanForScrollView:)];
and then:
- (void)handlePanForScrollView:(UIPanGestureRecognizer *)gesture {
CGPoint positionInView = [gesture locationInView:self.scrollView];
}
I want to move a UIView inside of a circle. The UIView moves every point inside the circle but not touch border line of the circle. I am calculating distance between circle and the UIView.
var distance = sqrt(
pow((touchPoint.x - selfCenter.x), 2) + pow((touchPoint.y - selfCenter.y), 2)
)
And limiting the UIView movement towards out of the circle
if distance <= radius {
theUIView.center = touchPoint
}
The problem starts here, if touch move out from circle the UIView stuck at the border, inside the circle. That is why I am trying write else statement as far as I have tried this.
if distance <= radius {
theUIView.center = touchPoint
} else {
theUIView.center = CGPointMake(
touchPoint.x / distance * radius,
touchPoint.y / distance * radius
)
}
Question is, how I can keep the UIView inside the circle and keep moving if touches keep moving. A hint would be great.
There are similar questions here -like this- but did not helped.
Your else case looks wrong. If you want to "project" a point outside of the circle
onto the circle boundary then it should be
if distance <= radius {
theUIView.center = touchPoint
} else {
theUIView.center = CGPointMake(
selfCenter.x + (touchPoint.x - selfCenter.x) / distance * radius,
selfCenter.y + (touchPoint.y - selfCenter.y) / distance * radius
)
}
Remark: The distance can be more easily computed using the hypot() function:
var distance = hypot(touchPoint.x - selfCenter.x, touchPoint.y - selfCenter.y)
I am an iOS programming newbie (reading several books on the subject simultaneously) and I would like to develop a (yet another) word game.
Coming from Flash/Flex programming background I was first expecting the tiles to be best bundled as gif or png assets.
But then I have taken a look (by using iFunbox) at the popular word games (Lexulous, Wordament, Words with Friends, Ruzzle, ...) and none of them is doing that:
That is none of the many apps I've looked at includes any letter pieces as images.
So my question is what would be the recommended approach (in Xcode 5 and with no additional SDKs like Cocos2d or Sparrow) to create a letter tile for a word game?
On a tile I'd like to have
the center-aligned letter (obviously!),
then an index in a corner displaying the letter value
and then another index for a total word value
When touched I'd like to make the tile a bit larger and add a shadow underneath it.
Should my tile class be a UIView (can they be dragged around, grow and have shadows?)
Should I use a .nib file for the tile?
For dragging I have found a good suggestions already: Dragging an UIView inside UIScrollView
But what I really would like to know here is: if UIView would make a good tile (performance- and feature-wise) or should I go for another base class (like maybe some shapes)?
Yes UIView would be a good container.
Create a subclass of UIView, say TileView, put in some labels, image view and a button over it, override buttons UIControlEventTouchDown, UIControlEventTouchUpInside, UIControlEventTouchDragInside events to help U navigate the view in its parent. Put the TileView in some container (your view controller view or where U would like it to be) and this is basically it.
-(void)btnPressed:(id)sender withEvent:(UIEvent *) event
{
CGPoint point = [[[event allTouches] anyObject] locationInView:self.view];
dx = 0;
dy = 0;
oldPoint = point;
}
-(void)btnRelesed:(id)sender
{
// stop moving code
}
-(void)btnDragged:(id)sender withEvent:(UIEvent *)event
{
CGPoint point = [[[event allTouches] anyObject] locationInView:self.view];
dx = point.x - oldPoint.x;
dy = point.y - oldPoint.y;
oldPoint = point;
// set tile view center position using
CGPoint ptCenter;
ptCenter = self.view.center;
ptCenter.x = ptCenter.x + dx;
ptCenter.y = ptCenter.y + dy;
self.view.center = ptCenter;
}
self.view is your TileView and its self.view cause U have ovrriden UIView class ;)
In this wheel we have 6 pieces. The top of the wheel is the specific point. The specific point gives information of pieces. Now it gives information of the blue piece. So if i click on one of the piece for example purple, i need that purple piece to goes to the specific point and automatically go into the given information about the purple piece.
CGFloat topPositionAngle = radiansToDegrees(atan2(view.transform.a, view.transform.b));
-180 - pink
-120 - blue
-60 - orange
0 - purple
60 - yellow
120 - green
Now the topPositionAngle shows -120 = blue, when purple comes to the specific point it shows 0.
UITouch *touch = [touches anyObject];
CGPoint currentTouchPoint = [touch locationInView:view];
CGFloat currentAngle = radiansToDegrees(atan2(currentTouchPoint.x, currentTouchPoint.y));
CGFloat angleTransform = ???
CGAffineTransform current = view.transform;
[UIView animateWithDuration:0.2f animations:^{
[view setTransform:CGAffineTransformRotate(current, angleTransform)];
}];
How can we get the automatically rotation to the specific point? Just like Dansk Bank app (see the following youtube link) something similar to the video from 0:21 - 0:25 min.
http://www.youtube.com/watch?v=hulBh_KNGjE
float fromAngle = atan2(m_locationBegan.y-img.center.y, m_locationBegan.x-img.center.x);
float toAngle = atan2(_location.y-imgDigits.center.y, _location.x-img.center.x);
float newAngle = wrapd(m_currentAngle + (toAngle - fromAngle), 0, 2*3.14);
angle = newAngle;
CGAffineTransform transform = CGAffineTransformMakeRotation(newAngle);
img.transform = transform;
Use something like this. Hope this helps
Here u should know touch point and your specific point
You Need to Know touched Point Angle:
CGFloat touchedPointAngle = atan2f(touchPoint.y - centerSuper.y, touchPoint.x - centerSuper.x) + M_PI;
if ((touchedPointAngle < sliceInRadians) && (touchedPointAngle > 0)) {
angleTransform = sliceInRadians;
} else if ...
Then you can transform.