I am trying to programmatically create buttons for a interactive element that is part of my app. The interface looks like a doughnut chart, and if the user touches one of the regions in the doughnut chart, it will highlight that region and all of the others. I am not so concerned right now with the colors, I am trying to get the seven buttons to appear. Here is a code sample of how I am trying to create the buttons:
var radius = (Frame.Width - 38) / 2.0f;
var innerRadius = radius - 32;
var diameter = radius * 2;
energyCircleView = new UIView();
energyCircleView.BackgroundColor = UIColor.White;
energyCircleView.Frame = new RectangleF(19, confirmEntryButton.Frame.Top - 16 - (diameter), diameter, diameter);
energyCircleView.Layer.CornerRadius = radius;
energyCircleView.Layer.MasksToBounds = true;
var center = new PointF(energyCircleView.Frame.X + radius, energyCircleView.Frame.Y + radius);
var ninety = MathHelper.TwoPi * (90.0f / 360.0f);
var seventh = MathHelper.TwoPi * (1.0f / 7.0f) * 360.0f;
for (int i = 0; i < 7; i++)
{
var startAngle = ninety + (i * seventh);
var endAngle = ninety + ((i + 1) * seventh);
var shapePath = new CGPath();
shapePath.AddArc(center.X, center.Y, radius, startAngle, endAngle, false);
shapePath.AddArc(center.X, center.Y, innerRadius, endAngle, startAngle, true);
var shapeLayer = new CAShapeLayer();
shapeLayer.Path = shapePath;
var button = new UIButton();
button.Frame = new RectangleF(0, 0, diameter, diameter);
button.BackgroundColor = MyColors[i];
button.Layer.Mask = shapeLayer;
button.Layer.MasksToBounds = true;
energyButtons.Add(button);
}
foreach (UIButton button in energyButtons)
{
energyCircleView.AddSubview(button);
}
You are adding buttons as a subview of the view. But you do not show the view itself.
Add the following code to show energyCircleView:
this.View.AddSubview(energyCircleView);
Related
Is there an options to round the corners on inside like in the picture? I have tried with CutCornerShape but it cuts them straight.
thy something like this
#Composable
private fun DrawTicketPathWithArc() {
Canvas(modifier = canvasModifier) {
val canvasWidth = size.width
val canvasHeight = size.height
// Black background
val ticketBackgroundWidth = canvasWidth * .8f
val horizontalSpace = (canvasWidth - ticketBackgroundWidth) / 2
val ticketBackgroundHeight = canvasHeight * .8f
val verticalSpace = (canvasHeight - ticketBackgroundHeight) / 2
// Get ticket path for background
val path1 = ticketPath(
topLeft = Offset(horizontalSpace, verticalSpace),
size = Size(ticketBackgroundWidth, ticketBackgroundHeight),
cornerRadius = 20.dp.toPx()
)
drawPath(path1, color = Color.Black)
// Dashed path in foreground
val ticketForegroundWidth = ticketBackgroundWidth * .95f
val horizontalSpace2 = (canvasWidth - ticketForegroundWidth) / 2
val ticketForegroundHeight = ticketBackgroundHeight * .9f
val verticalSpace2 = (canvasHeight - ticketForegroundHeight) / 2
// Get ticket path for background
val path2 = ticketPath(
topLeft = Offset(horizontalSpace2, verticalSpace2),
size = Size(ticketForegroundWidth, ticketForegroundHeight),
cornerRadius = 20.dp.toPx()
)
drawPath(
path2,
color = Color.Red,
style = Stroke(
width = 2.dp.toPx(),
pathEffect = PathEffect.dashPathEffect(
floatArrayOf(20f, 20f)
)
)
)
}
}
check this page
I found a solution which looks like that
#Composable
private fun DrawDashLine() {
val pathEffect = PathEffect.dashPathEffect(floatArrayOf(12f, 10f), 0f)
Canvas(modifier = Modifier.fillMaxWidth()) {
drawLine(
color = Color.Black,
strokeWidth = 3f,
start = Offset(0f, 0f),
end = Offset(size.width, 0f),
pathEffect = pathEffect
)
drawCircle(
color = Color.Black,
center = Offset(x = 1f, y = 2f),
radius = 20f
)
drawCircle(
color = Color.Black,
center = Offset(x = size.width, y = 2f),
radius = 20f
)
}
}
The background must be black, and the front is white and the round circles just come above the white.
Currently, I'm using offset and rotation to position elements in KonvaJS in a circle. Is there another method that would still layout the elements in a circle without rotating the text (eg like a clock.)
Output looks like this:
Code looks like this:
function drawNumber(radius, number, step) {
var segmentDegree = 360/16
var rotation = -90 + step * segmentDegree
var label = new Konva.Text({
x: patternOriginX,
y: patternOriginY,
text: number.toString(),
fontSize: 12,
fill: '#636363',
rotation: rotation
});
label.offsetX(-radius)
return label
}
You can use trigonometry to find the position of the text on its angle:
var centerX = stage.width() / 2;
var centerY = stage.height() / 2;
var QUANTITY = 10;
var RADIUS = 50;
var dAlhpa = Math.PI * 2 / QUANTITY;
for (var i = 0; i < QUANTITY; i++) {
var alpha = dAlhpa * i;
var dx = Math.cos(alpha) * RADIUS;
var dy = Math.sin(alpha) * RADIUS;
layer.add(new Konva.Text({
x: centerX + dx,
y: centerY + dy,
text: i.toString()
}))
}
Demo: https://jsbin.com/fizucotaxe/1/edit?html,js,output
I need to find nearest point from given CLLocationCoordinate2D on array of GMSPolyline. I can convert this to GMSPath if that's better. Is there any ready method (or any repository) for such calculations? I have some problems with implementation. I wonder how to create an algorithm:
1. for all polylines
1.1. find smallest distance between polyline and touch point, save CLLocationCoordinate2D
2. for all distances from point 1.1.
2.1. find the shortest one, it's CLLocationCoordinate2D is our point
Now the question is how to achieve point 1.1..?
Basing on SOF shortest distance question, I wrote such code:
- (void)findNearestLineSegmentToCoordinate:(CLLocationCoordinate2D)coordinate {
GMSPolyline *bestPolyline;
double bestDistance = DBL_MAX;
CGPoint originPoint = CGPointMake(coordinate.longitude, coordinate.latitude);
for (GMSPolyline *polyline in self.polylines) {
polyline.strokeColor = [UIColor redColor]; // TMP
if (polyline.path.count < 2) { // we need at least 2 points: start and end
return;
}
for (NSInteger index = 0; index < polyline.path.count - 1; index++) {
CLLocationCoordinate2D startCoordinate = [polyline.path coordinateAtIndex:index];
CGPoint startPoint = CGPointMake(startCoordinate.longitude, startCoordinate.latitude);
CLLocationCoordinate2D endCoordinate = [polyline.path coordinateAtIndex:(index + 1)];
CGPoint endPoint = CGPointMake(endCoordinate.longitude, endCoordinate.latitude);
double distance = [self distanceToPoint:originPoint fromLineSegmentBetween:startPoint and:endPoint];
if (distance < bestDistance) {
bestDistance = distance;
bestPolyline = polyline;
}
}
}
bestPolyline.map = nil;
bestPolyline.strokeColor = [UIColor greenColor]; // TMP
bestPolyline.map = self.aView.mapView;
}
Still, the problem is with exact point. Any algorithm? I'll post answer here when found.
Ok, I've managed to write it. Method nearestPointToPoint:onLineSegmentPointA:pointB:distance: allows you both to find closest coordinate and distance between selected point and segment line (so line with start and end).
- (CLLocationCoordinate2D)nearestPolylineLocationToCoordinate:(CLLocationCoordinate2D)coordinate {
GMSPolyline *bestPolyline;
double bestDistance = DBL_MAX;
CGPoint bestPoint;
CGPoint originPoint = CGPointMake(coordinate.longitude, coordinate.latitude);
for (GMSPolyline *polyline in self.polylines) {
if (polyline.path.count < 2) { // we need at least 2 points: start and end
return kCLLocationCoordinate2DInvalid;
}
for (NSInteger index = 0; index < polyline.path.count - 1; index++) {
CLLocationCoordinate2D startCoordinate = [polyline.path coordinateAtIndex:index];
CGPoint startPoint = CGPointMake(startCoordinate.longitude, startCoordinate.latitude);
CLLocationCoordinate2D endCoordinate = [polyline.path coordinateAtIndex:(index + 1)];
CGPoint endPoint = CGPointMake(endCoordinate.longitude, endCoordinate.latitude);
double distance;
CGPoint point = [self nearestPointToPoint:originPoint onLineSegmentPointA:startPoint pointB:endPoint distance:&distance];
if (distance < bestDistance) {
bestDistance = distance;
bestPolyline = polyline;
bestPoint = point;
}
}
}
return CLLocationCoordinate2DMake(bestPoint.y, bestPoint.x);
}
Method nearestPolylineLocationToCoordinate: will browse through all polylines (you just need to supply polylines array == self.polylines) and find the best one.
// taken and modified from: http://stackoverflow.com/questions/849211/shortest-distance-between-a-point-and-a-line-segment
- (CGPoint)nearestPointToPoint:(CGPoint)origin onLineSegmentPointA:(CGPoint)pointA pointB:(CGPoint)pointB distance:(double *)distance {
CGPoint dAP = CGPointMake(origin.x - pointA.x, origin.y - pointA.y);
CGPoint dAB = CGPointMake(pointB.x - pointA.x, pointB.y - pointA.y);
CGFloat dot = dAP.x * dAB.x + dAP.y * dAB.y;
CGFloat squareLength = dAB.x * dAB.x + dAB.y * dAB.y;
CGFloat param = dot / squareLength;
CGPoint nearestPoint;
if (param < 0 || (pointA.x == pointB.x && pointA.y == pointB.y)) {
nearestPoint.x = pointA.x;
nearestPoint.y = pointA.y;
} else if (param > 1) {
nearestPoint.x = pointB.x;
nearestPoint.y = pointB.y;
} else {
nearestPoint.x = pointA.x + param * dAB.x;
nearestPoint.y = pointA.y + param * dAB.y;
}
CGFloat dx = origin.x - nearestPoint.x;
CGFloat dy = origin.y - nearestPoint.y;
*distance = sqrtf(dx * dx + dy * dy);
return nearestPoint;
}
You can use it eg in:
- (void)mapView:(GMSMapView *)mapView didEndDraggingMarker:(GMSMarker *)marker {
marker.position = [self nearestPolylineLocationToCoordinate:marker.position];
}
#Vive's nearestPointToPoint() to Swift 5
func nearestPointToPoint(_ origin: CGPoint, _ pointA: CGPoint, _ pointB: CGPoint) -> (CGPoint, Double) {
let dAP = CGPoint(x: origin.x - pointA.x, y: origin.y - pointA.y)
let dAB = CGPoint(x: pointB.x - pointA.x, y: pointB.y - pointA.y)
let dot = dAP.x * dAB.x + dAP.y * dAB.y
let squareLength = dAB.x * dAB.x + dAB.y * dAB.y
let param = dot / squareLength
// abnormal value at near latitude 180
// var nearestPoint = CGPoint()
// if param < 0 || (pointA.x == pointB.x && pointA.y == pointB.y) {
// nearestPoint.x = pointA.x
// nearestPoint.y = pointA.y
// } else if param > 1 {
// nearestPoint.x = pointB.x
// nearestPoint.y = pointB.y
// } else {
// nearestPoint.x = pointA.x + param * dAB.x
// nearestPoint.y = pointA.y + param * dAB.y
// }
let nearestPoint = CGPoint(x: pointA.x + param * dAB.x,
y: pointA.y + param * dAB.y)
let dx = origin.x - nearestPoint.x
let dy = origin.y - nearestPoint.y
let distance = sqrtf(Float(dx * dx + dy * dy))
return (nearestPoint, Double(distance))
}
Result is
me: (53, -1), pointA: (52, -2), pointB(52, 0) => (52, -1)
me: (53, 0), pointA: (52, -1), pointB(52, 1) => (52, 0)
me: (53, 1), pointA: (52, 0), pointB(52, 2) => (52, 1)
me: (-1, -77), pointA: (0, -78), pointB(-2, -78) => (-1, -78)
me: (0, -77), pointA: (-1, -78), pointB(1, -78) => (0, -78)
me: (1, -77), pointA: (2, -78), pointB(0, -78) => (1, -78)
me: (1, 179), pointA: (0, 178), pointB(0, 180) => (0, 179)
me: (1, 180), pointA: (0, 179), pointB(0, -179) => (0, 180)
me: (1, -179), pointA: (0, 180), pointB(0, -178) => (0, -179)
But some error at latitude 180. I can't fix it.
me: (0, 180), pointA: (0, 179), pointB(1, 180) => (0.5, 179.5)
me: (0, -179), pointA: (0, 179), pointB(1, -179) => (0.99, -178.99) // abnormal
I am creating an app that is required to be able to swipe between views. I used the sample code from here to get started. The issue I am having is that when I go from one ViewController to the paged ViewControllers then back again the ViewControllers are added to the list and the list keeps growing. I get duplicate ViewControllers on the screen that I can scroll through. The new list is just added to the end of the already populated viewControllers.
I am not using a navigation controller on the home screen it is simply a scroll view with buttons on it. This is part of the HomeViewController class:
private void BuildScrollView()
{
float padding = 0.0f;
float h = 80.0f;
UIView content = new UIView (new RectangleF (15, 130, UIScreen.MainScreen.Bounds.Width - 30, UIScreen.MainScreen.Bounds.Height));
content.BackgroundColor = UIColor.FromRGB(ColorManager.Grey_undrline_RGB[0], ColorManager.Grey_undrline_RGB[1], ColorManager.Grey_undrline_RGB[2]);
UIScrollView scrollView = new UIScrollView {
Frame = new RectangleF(2, 2, content.Frame.Width - 4 , content.Frame.Height -4),
BackgroundColor = UIColor.FromRGB(ColorManager.Grey_backgrd_RGB[0], ColorManager.Grey_backgrd_RGB[1], ColorManager.Grey_backgrd_RGB[2]),
AutoresizingMask = UIViewAutoresizing.FlexibleWidth
};
List<myObject> prayerList = myObjectBuilder.GeObjectFiles ();
for(int i = 0; i < myList.Count; i++){
UIView btnView = new UIView(new RectangleF (padding , padding * (i + 1) + (i * h), scrollView.Frame.Width - (padding * 2), h));
btnView.BackgroundColor = UIColor.FromRGB (ColorManager.Grey_backgrd_RGB [0], ColorManager.Grey_backgrd_RGB [1], ColorManager.Grey_backgrd_RGB [2]);
myButton btnImage = new myButton (new RectangleF (3, 3, 135, 80));
UIImage tempImage = new UIImage ("Images/bottomImages/" + myList[i].bottomImage ).Scale(new SizeF(135, 80));
btnImage.BackgroundColor = UIColor.FromPatternImage (tempImage);
btnImage.PrayerID = i;
btnImage.TouchUpInside += (object sender, EventArgs e) => {
LoadPrayerView(sender);
};
btnView.AddSubview (btnImage);
myButton button = new myButton(new RectangleF (141, 3, btnView.Frame.Width - 145, h));
button.PrayerID = i;
button.BackgroundColor = UIColor.White;
button.SetTitle (myList[i].ShortTitle.ToUpper(), UIControlState.Normal);
button.SetTitleColor (UIColor.Black, UIControlState.Normal);
button.HorizontalAlignment = UIControlContentHorizontalAlignment.Left;
button.TitleLabel.Font = UIFont.FromName ("HelveticaNeue", 16.0f);
button.TitleLabel.Lines = 0;
button.TitleLabel.LineBreakMode = UILineBreakMode.WordWrap;
button.TitleEdgeInsets = new UIEdgeInsets (0, 10, 0, 10);
button.TouchUpInside += (object sender, EventArgs e) => {
LoadView(sender);
};
btnView.AddSubview (button);
scrollView.AddSubview (btnView);
}
scrollView.ContentSize = new SizeF (scrollView.Frame.Width, (prayerList.Count + 2) * (h + padding) + padding);
content.AddSubview (scrollView);
this.View.AddSubview (content);
}
I can post more code if needed. At this point I am not really sure where to start looking for a solution.
I'm trying to find a rotated rectangle UIView's four corners' coordinates.
I think one way I can do is to use recognizer.rotation, find the rotated angle then calculate the origins. But that requires some geometry calculation.
- (IBAction)handlePan:(UIRotationGestureRecognizer*)recognizer {
NSLog(#"Rotation in degrees since last change: %f", [recognizer rotation] * (180 / M_PI));
recognizer.view.transform = CGAffineTransformRotate(recognizer.view.transform, recognizer.rotation);
NSLog(#"%#",recognizer);
recognizer.rotation = 0;
NSLog(#"bound is %f and %f, frame is %f and %f, %f and %f.",recognizer.view.bounds.size.width,recognizer.view.bounds.size.height, recognizer.view.frame.size.width,recognizer.view.frame.size.height, recognizer.view.frame.origin.x, recognizer.view.frame.origin.y);
}
I'm just wondering if there are any other easier ways to get the coordinates?
Thanks!
EDIT:
Looks like we have a great answer here(see answer below). I have managed to calculate the corners through a stupid way -- using rotation angle and geometry. It works but not easy and light. I'm sharing my code here just in case some one may want to use it(Even though I doubt it.)
float r = 100;
NSLog(#"radius is %f.",r);
float AAngle = M_PI/3+self.rotatedAngle;
float AY = recognizer.view.center.y - sin(AAngle)*r;
float AX = recognizer.view.center.x - cos(AAngle)*r;
self.pointPADA = CGPointMake(AX, AY);
NSLog(#"View Center is (%f,%f)",recognizer.view.center.x,recognizer.view.center.y);
NSLog(#"Point A has coordinate (%f,%f)",self.pointPADA.x,self.pointPADA.y);
float BAngle = M_PI/3-self.rotatedAngle;
float BY = recognizer.view.center.y - sin(BAngle)*r;
float BX = recognizer.view.center.x + cos(BAngle)*r;
self.pointPADB = CGPointMake(BX, BY);
NSLog(#"Point B has coordinate (%f,%f)",BX,BY);
float CY = recognizer.view.center.y + sin(AAngle)*r;
float CX = recognizer.view.center.x + cos(AAngle)*r;
self.pointPADC = CGPointMake(CX, CY);
NSLog(#"Point C has coordinate (%f,%f)",CX,CY);
float DY = recognizer.view.center.y + sin(BAngle)*r;
float DX = recognizer.view.center.x - cos(BAngle)*r;
self.pointPADD = CGPointMake(DX, DY);
NSLog(#"Point D has coordinate (%f,%f)",DX,DY);
Here's my solution though I wonder if there's a more succinct way:
CGPoint originalCenter = CGPointApplyAffineTransform(theView.center,
CGAffineTransformInvert(theView.transform));
CGPoint topLeft = originalCenter;
topLeft.x -= theView.bounds.size.width / 2;
topLeft.y -= theView.bounds.size.height / 2;
topLeft = CGPointApplyAffineTransform(topLeft, theView.transform);
CGPoint topRight = originalCenter;
topRight.x += theView.bounds.size.width / 2;
topRight.y -= theView.bounds.size.height / 2;
topRight = CGPointApplyAffineTransform(topRight, theView.transform);
CGPoint bottomLeft = originalCenter;
bottomLeft.x -= theView.bounds.size.width / 2;
bottomLeft.y += theView.bounds.size.height / 2;
bottomLeft = CGPointApplyAffineTransform(bottomLeft, theView.transform);
CGPoint bottomRight = originalCenter;
bottomRight.x += theView.bounds.size.width / 2;
bottomRight.y += theView.bounds.size.height / 2;
bottomRight = CGPointApplyAffineTransform(bottomRight, theView.transform);
Checked answer in Swift
struct ViewCorners {
private(set) var topLeft: CGPoint!
private(set) var topRight: CGPoint!
private(set) var bottomLeft: CGPoint!
private(set) var bottomRight: CGPoint!
private let originalCenter: CGPoint
private let transformedView: UIView
private func pointWith(multipliedWidth: CGFloat, multipliedHeight: CGFloat) -> CGPoint {
var x = originalCenter.x
x += transformedView.bounds.width / 2 * multipliedWidth
var y = originalCenter.y
y += transformedView.bounds.height / 2 * multipliedHeight
var result = CGPoint(x: x, y: y).applying(transformedView.transform)
result.x += transformedView.transform.tx
result.y += transformedView.transform.ty
return result
}
init(view: UIView) {
transformedView = view
originalCenter = view.center.applying(view.transform.inverted())
topLeft = pointWith(multipliedWidth:-1, multipliedHeight:-1)
topRight = pointWith(multipliedWidth: 1, multipliedHeight:-1)
bottomLeft = pointWith(multipliedWidth:-1, multipliedHeight: 1)
bottomRight = pointWith(multipliedWidth: 1, multipliedHeight: 1)
}
}
Then create struct instance and take transformed rect corners new points.
let view = UIView(frame: CGRect(x: 40, y: 20, width: 100, height: 50))
view.transform = .init(rotationAngle: .pi / 4)
let corners = ViewCorners(view: view)
print(corners.topLeft,
corners.topRight,
corners.bottomLeft,
corners.bottomRight,
separator: "\n")
Using Swift 4, We can get the bounds of rotated view by simple way.
let transformedBounds = view.bounds.applying(view.transform)