iOS drag and drop within parent bounds - ios

I'm try to implement drag and drop for some UIImageViews within a UIView. It is currently working, with the exception that you can drag the image views out of the parent and off the screen. I know I have to check to see if the views go outside the parent bounds.. but im struggling to achieve it! Does anyone have any experience with this? For the Windows Phone client, all that is needed is the following line;
var dragBehavior = new MouseDragElementBehavior {ConstrainToParentBounds = true};
The current gesture code I have is here;
private UIPanGestureRecognizer CreateGesture(UIImageView imageView)
{
float dx = 0;
float dy = 0;
var panGesture = new UIPanGestureRecognizer((pg) =>
{
if ((pg.State == UIGestureRecognizerState.Began || pg.State == UIGestureRecognizerState.Changed) && (pg.NumberOfTouches == 1))
{
var p0 = pg.LocationInView(View);
if (dx == 0)
dx = (float)p0.X - (float)imageView.Center.X;
if (dy == 0)
dy = (float)p0.Y - (float)imageView.Center.Y;
float newX = (float)p0.X - dx;
float newY = (float)p0.Y - dy;
var p1 = new PointF(newX, newY);
imageView.Center = p1;
}
else if (pg.State == UIGestureRecognizerState.Ended)
{
dx = 0;
dy = 0;
}
});
return panGesture;
}

I resolved this with the following code;
var panGesture = new UIPanGestureRecognizer((pg) =>
{
if ((pg.State == UIGestureRecognizerState.Began || pg.State == UIGestureRecognizerState.Changed) && (pg.NumberOfTouches == 1))
{
var p0 = pg.LocationInView(View);
if (dx == 0)
dx = (float)p0.X - (float)imageView.Center.X;
if (dy == 0)
dy = (float)p0.Y - (float)imageView.Center.Y;
float newX = (float)p0.X - dx;
float newY = (float)p0.Y - dy;
var p1 = new PointF(newX, newY);
// If too far right...
if (p1.X > imageView.Superview.Bounds.Size.Width - (imageView.Bounds.Size.Width / 2))
p1.X = (float) imageView.Superview.Bounds.Size.Width - (float)imageView.Bounds.Size.Width / 2;
else if (p1.X < 0 + (imageView.Bounds.Size.Width / 2)) // If too far left...
p1.X = 0 + (float)(imageView.Bounds.Size.Width / 2);
// If too far down...
if (p1.Y > imageView.Superview.Bounds.Size.Height - (imageView.Bounds.Size.Height / 2))
p1.Y = (float)imageView.Superview.Bounds.Size.Height - (float)imageView.Bounds.Size.Height / 2;
else if (p1.Y < 0 + (imageView.Bounds.Size.Height / 2)) // If too far up...
p1.Y = 0 + (float)(imageView.Bounds.Size.Height / 2);
imageView.Center = p1;
}
else if (pg.State == UIGestureRecognizerState.Ended)
{
// reset offsets when dragging ends so that they will be recalculated for next touch and drag that occurs
dx = 0;
dy = 0;
}
});

Related

Zooming in on a UI element causes it to become unsharp/pixelated on iOS

I am using Xamarin.Forms.
On iOS, when using the pinch gesture below to zoom in on a Label (for example), the Label turns pixelated instead of updating and staying sharp.
On Android, this doesn't happen and the Label stays sharp and visible, no matter how much I zoom in on it.
Is there a way to make behave on iOS as it does with Android?
Edit: this behaviour can also be observed simply by raising the scale of a visual element, on Android it looks good but on iOS it turns pixelated. I assume my problem lies here but I'm not sure how to tackle it yet.
The pinch gesture implementation I use to zoom in with:
public class ZoomWithPinch : ContentView
{
public ZoomWithPinch()
{
var pinchGesture = new PinchGestureRecognizer();
pinchGesture.PinchUpdated += OnPinchUpdated;
GestureRecognizers.Add(pinchGesture);
}
double currentScale = 1;
double startScale = 1;
double xOffset = 0;
double yOffset = 0;
private void OnPinchUpdated(object sender, PinchGestureUpdatedEventArgs e)
{
if (e.Status == GestureStatus.Started)
{
startScale = Content.Scale;
Content.AnchorX = 0;
Content.AnchorY = 0;
}
if (e.Status == GestureStatus.Running)
{
currentScale += (e.Scale - 1) * startScale;
currentScale = Math.Max(1, currentScale);
double renderedX = Content.X + xOffset;
double deltaX = renderedX / Width;
double deltaWidth = Width / (Content.Width * startScale);
double originX = (e.ScaleOrigin.X - deltaX) * deltaWidth;
double renderedY = Content.Y + yOffset;
double deltaY = renderedY / Height;
double deltaHeight = Height / (Content.Height * startScale);
double originY = (e.ScaleOrigin.Y - deltaY) * deltaHeight;
double targetX = xOffset - (originX * Content.Width) * (currentScale - startScale);
double targetY = yOffset - (originY * Content.Height) * (currentScale - startScale);
Content.TranslationX = Math.Min(0, Math.Max(targetX, -Content.Width * (currentScale - 1)));
Content.TranslationY = Math.Min(0, Math.Max(targetY, -Content.Height * (currentScale - 1)));
Content.Scale = currentScale;
}
if (e.Status == GestureStatus.Completed)
{
xOffset = Content.TranslationX;
yOffset = Content.TranslationY;
}
}
public void OnPanUpdated(object sender, PanUpdatedEventArgs e)
{
if (Content.Scale == 1)
{
return;
}
switch (e.StatusType)
{
case GestureStatus.Running:
double newX = (e.TotalX * Scale) + xOffset;
double newY = (e.TotalY * Scale) + yOffset;
double width = (Content.Width * Content.Scale);
double height = (Content.Height * Content.Scale);
bool canMoveX = width > Application.Current.MainPage.Width;
bool canMoveY = height > Application.Current.MainPage.Height;
if (canMoveX)
{
double minX = (width - (Application.Current.MainPage.Width / 2)) * -1;
double maxX = Math.Min(Application.Current.MainPage.Width / 2, width / 2);
if (newX < minX)
{
newX = minX;
}
if (newX > maxX)
{
newX = maxX;
}
}
else
{
newX = 0;
}
if (canMoveY)
{
double minY = (height - (Application.Current.MainPage.Height / 2)) * -1;
double maxY = Math.Min(Application.Current.MainPage.Width / 2, height / 2);
if (newY < minY)
{
newY = minY;
}
if (newY > maxY)
{
newY = maxY;
}
}
else
{
newY = 0;
}
Content.TranslationX = newX;
Content.TranslationY = newY;
break;
case GestureStatus.Completed:
xOffset = Content.TranslationX;
yOffset = Content.TranslationY;
break;
}
}
}
<ZoomWithPinch>
<Grid>
<Label Text="Test" HorizontalOptions="Center" VerticalOptions="Center"/>
</Grid>
</ZoomWithPinch>

XamForms.Controls.Calendar Special Dates Text Blurry

I'm using XamForms.Controls.Calendar in my Xamarin.Forms project everything works great, but the texts entered in the "Text" section of the SpeacialDates property appear blurred on the iOS platform, how can I solve it?
DrawText feature ios also have the same way, despite the intense research of this feature could not solve the blur problem
https://imgur.com/a/E0UZnD9 "Screenshot"
protected void DrawText(CGContext g, Pattern p, CGRect r)
{
if (string.IsNullOrEmpty(p.Text)) return;//Copperplate-Light" KohinoorTelugu-Light
string fontname = Device.Idiom == TargetIdiom.Tablet ? "KohinoorTelugu-Light" : "GillSans-UltraBold";//GillSans-UltraBold
var bounds = p.Text.StringSize(UIFont.FromName(fontname, p.TextSize));
//"GillSans-UltraBold
var al = (int)p.TextAlign;
var x = r.X;
if ((al & 2) == 2) // center
{
x = r.X + (int)Math.Round(r.Width / 2.0) - (int)Math.Round(bounds.Width / 2.0);
}
else if ((al & 4) == 4) // right
{
x = (r.X + r.Width) - bounds.Width - 2;
}
var y = r.Y + (int)Math.Round(bounds.Height / 2.0) + 2;
if ((al & 16) == 16) // middle
{
y = r.Y + (int)Math.Ceiling(r.Height / 2.0) + (int)Math.Round(bounds.Height / 5.0);
}
else if ((al & 32) == 32) // bottom
{
y = (r.Y + r.Height) - 2;
}
g.SaveState();
g.SetShouldAntialias(true);
g.SetShouldSmoothFonts(true);
g.SetAllowsFontSmoothing(true);
g.SetAllowsAntialiasing(true);
g.SetShouldSubpixelPositionFonts(false);
g.InterpolationQuality = CGInterpolationQuality.High;
g.SetAllowsSubpixelPositioning(false);
g.SetAllowsFontSubpixelQuantization(false);
g.InterpolationQuality = CGInterpolationQuality.High;
g.TranslateCTM(0, Bounds.Height);
g.ScaleCTM(1, -1);
g.SetStrokeColor(p.TextColor.ToCGColor());
g.DrawPath(CGPathDrawingMode.EOFillStroke);
g.SetFillColor(p.TextColor.ToCGColor());
g.SetTextDrawingMode(CGTextDrawingMode.FillStrokeClip);
g.SelectFont(fontname, p.TextSize, CGTextEncoding.MacRoman);
g.ShowTextAtPoint(x, Bounds.Height - y, p.Text);
g.RestoreState();
}
}

Get area of intersecting line (CGPoints)

I have an Array of CGPoints and I would like to find those points, which build a shape. Please see the attached image:
The red circles just mark the points I have.
How can the area with the question mark be found?
Thanks.
You are going to have to start with your first line segment and check for intersections. Obviously if the first two line segments intersect then they are the same line and your shape is just a line, so ignore that case. As you continue down your line segments once you find a segment pair that intersect then you have your shape.
Check line segment 2 against line segment 1. Then check line segment 3 against line segment 2, then against line segment 1. Then check 4 against 3, then 2, then 1, etc... If you find that line segment 7 intersects with line segment 3, delete the first point of line segment 3 and se it to the intersection point you found. Then delete the last point of line segment 7 and set it to the intersection point you found. There you have your shape.
Here is an example method to find the intersection of 2 line segments (written in C#, but it's straight math so it should be very easy to convert to any language you would like). Taken from here:
// Determines if the lines AB and CD intersect.
static bool LinesIntersect(PointF A, PointF B, PointF C, PointF D)
{
PointF CmP = new PointF(C.X - A.X, C.Y - A.Y);
PointF r = new PointF(B.X - A.X, B.Y - A.Y);
PointF s = new PointF(D.X - C.X, D.Y - C.Y);
float CmPxr = CmP.X * r.Y - CmP.Y * r.X;
float CmPxs = CmP.X * s.Y - CmP.Y * s.X;
float rxs = r.X * s.Y - r.Y * s.X;
if (CmPxr == 0f)
{
// Lines are collinear, and so intersect if they have any overlap
return ((C.X - A.X < 0f) != (C.X - B.X < 0f))
|| ((C.Y - A.Y < 0f) != (C.Y - B.Y < 0f));
}
if (rxs == 0f)
return false; // Lines are parallel.
float rxsr = 1f / rxs;
float t = CmPxs * rxsr;
float u = CmPxr * rxsr;
return (t >= 0f) && (t <= 1f) && (u >= 0f) && (u <= 1f);
}
I've figured out the solution.
This function returns a polygon for each area that gets closed by intersecting lines.
func intersectionOfLineFrom(p1: CGPoint, to p2: CGPoint, withLineFrom p3: CGPoint, to p4: CGPoint) -> NSValue? {
let d: CGFloat = (p2.x - p1.x) * (p4.y - p3.y) - (p2.y - p1.y) * (p4.x - p3.x)
if d == 0 {
return nil
}
// parallel lines
let u: CGFloat = ((p3.x - p1.x) * (p4.y - p3.y) - (p3.y - p1.y) * (p4.x - p3.x)) / d
let v: CGFloat = ((p3.x - p1.x) * (p2.y - p1.y) - (p3.y - p1.y) * (p2.x - p1.x)) / d
if u < 0.0 || u > 1.0 {
return nil
}
// intersection point not between p1 and p2
if v < 0.0 || v > 1.0 {
return nil
}
// intersection point not between p3 and p4
var intersection: CGPoint = CGPointZero
intersection.x = p1.x + u * (p2.x - p1.x)
intersection.y = p1.y + u * (p2.y - p1.y)
return NSValue(CGPoint: intersection)
}
func intersectedPolygons(points: [CGPoint]) -> [[CGPoint]] {
var removeIndexBelow : Int = 0
var removeIndexAbove : Int = 0
var resultArrays : [[CGPoint]] = [[CGPoint]]()
for i in 1..<points.count {
let firstLineStart = points[i-1] as CGPoint
let firstLineEnd = points[i] as CGPoint
for var j = points.count-1; j > i+1; j-- {
let lastLineStart = points[j-1] as CGPoint
let lastLineEnd = points[j] as CGPoint
if let intersect: NSValue = self.intersectionOfLineFrom(firstLineStart, to: firstLineEnd, withLineFrom: lastLineStart, to: lastLineEnd){
var pointsCopy = points
let intersection = intersect.CGPointValue()
pointsCopy[i-1] = intersection
pointsCopy[j] = intersection
removeIndexBelow = i
removeIndexAbove = j
let fullPoly = Array(pointsCopy[removeIndexBelow-1..<removeIndexAbove])
resultArrays.append(fullPoly)
break;
}
}
}
return resultArrays
}

Find the precise centroid (as an MKMapPoint) of an MKPolygon

This means excluding the area(s) of any interiorPolygons.
Once one has the centroid of the outer points polygon, how does one (i.e., in the form of an Objective-C example) adjust the centroid by the subtractive interiorPolygons? Or is there a more elegant way to compute the centroid in one go?
If you help get the code working, it will be open sourced (WIP here).
Might be helpful:
http://www.ecourses.ou.edu/cgi-bin/eBook.cgi?topic=st&chap_sec=07.2&page=case_sol
https://en.wikipedia.org/wiki/Centroid#Centroid_of_polygon
Thinking about it today, it makes qualitative sense that adding each interior centroid weighted by area to the exterior centroid would arrive at something sensible. (A square with an interior polygon (hole) on the left side would displace the centroid right, directly proportional to the area of the hole.)
Not to scale:
- (MKMapPoint)calculateCentroid
{
switch (self.pointCount) {
case 0: return MKMapPointMake(0.0,
0.0);
case 1: return MKMapPointMake(self.points[0].x,
self.points[0].y);
case 2: return MKMapPointMake((self.points[0].x + self.points[1].x) / 2.0,
(self.points[0].y + self.points[1].y) / 2.0);
}
// onward implies pointCount >= 3
MKMapPoint centroid;
MKMapPoint *previousPoint = &(self.points[self.pointCount-1]); // for i=0, wrap around to the last point
for (NSUInteger i = 0; i < self.pointCount; ++i) {
MKMapPoint *point = &(self.points[i]);
double delta = (previousPoint->x * point->y) - (point->x * previousPoint->y); // x[i-1]*y[i] + x[i]*y[i-1]
centroid.x += (previousPoint->x + point->x) * delta; // (x[i-1] + x[i]) / delta
centroid.y += (previousPoint->y + point->y) * delta; // (y[i-1] + y[i]) / delta
previousPoint = point;
}
centroid.x /= 6.0 * self.area;
centroid.y /= 6.0 * self.area;
// interiorPolygons are holes (subtractive geometry model)
for (MKPolygon *interiorPoly in self.interiorPolygons) {
if (interiorPoly.area == 0.0) {
continue; // avoid div-by-zero
}
centroid.x += interiorPoly.centroid.x / interiorPoly.area;
centroid.y += interiorPoly.centroid.y / interiorPoly.area;
}
return centroid;
}
in Swift 5
private func centroidForCoordinates(_ coords: [CLLocationCoordinate2D]) -> CLLocationCoordinate2D? {
guard let firstCoordinate = coordinates.first else {
return nil
}
guard coords.count > 1 else {
return firstCoordinate
}
var minX = firstCoordinate.longitude
var maxX = firstCoordinate.longitude
var minY = firstCoordinate.latitude
var maxY = firstCoordinate.latitude
for i in 1..<coords.count {
let current = coords[i]
if minX > current.longitude {
minX = current.longitude
} else if maxX < current.longitude {
maxX = current.longitude
} else if minY > current.latitude {
minY = current.latitude
} else if maxY < current.latitude {
maxY = current.latitude
}
}
let centerX = minX + ((maxX - minX) / 2)
let centerY = minY + ((maxY - minY) / 2)
return CLLocationCoordinate2D(latitude: centerY, longitude: centerX)
}

Keep moving images bouncing within frame width and height

I am trying to figure out how to keep an image within the frame width and height. Right now it just wraps around. I would preferably like to create something that stays within the frame and bounces around inside.
-(void) moveButterfly {
bfly.center = CGPointMake(bfly.center.x + bfly_vx, bfly.center.y + bfly_vy);
if(bfly.center.x > frameWidth)
{
bfly.center = CGPointMake(0, bfly.center.y + bfly_vy);
}
else if (bfly.center.x < 0)
{
bfly.center = CGPointMake(frameWidth, bfly.center.y + bfly_vy);
}
if(bfly.center.y > frameHeight)
{
bfly.center = CGPointMake(bfly.center.x + bfly_vx, 0);
}
else if (bfly.center.y < 0)
{
bfly.center = CGPointMake(bfly.center.x + bfly_vx, frameHeight);
}
}
-(void)moveButterfly{
static int dx = 1;
static int dy = 1;
if (bfly.frame.origin.x >= self.view.bounds.size.width - bfly.bounds.size.width) {
dx = -dx;
}
if (bfly.frame.origin.y >= self.view.bounds.size.height - bfly.bounds.size.height) {
dy = -dy;
}
if (bfly.frame.origin.x <= 0) {
dx = -dx;
}
if (bfly.frame.origin.y <= 0) {
dy = -dy;
}
CGPoint point = bfly.center;
point.x += dx;
point.y += dy;
bfly.center = point;
}
Keep Calling this function using NSTimer at the rate you want to update position. Here dx and dy is the velocity at which butterfly will move.

Resources