2016-11-15 9 views
5

Pracuję na wykresie kołowym i muszę dodać mały widok na końcu zaznaczonego koła (nie cienia), aby wskazać wartość docelową. Czy mogę znaleźć okrąg (podświetlenie) super widok x, y pozycje oparte na punkcie końcowym obrysu?Jak uzyskać pozycję podglądu w CAShapeLayer?

UIView->Circle (przy użyciu CAShapeLayer i UIBezierPath). Muszę uzyskać pozycję UIView na podstawie wartości obrysu kończącej się Circle.

Proszę odnieść ten link (podobnie jak 23% z linią przerywaną) http://support.softwarefx.com/media/74456678-5a6a-e211-84a5-0019b9e6b500/large

Z góry dzięki! Need to find green end circle position

Aktualizacja: Próbowałem alexburtnik kod faktycznie pracuję na wykresie zegara w Objective C, ale to nie jest problem. Próbowałem jak wspomniałem alexburtnik, uważam, że doskonale działa na wykresie zegara. Musimy wprowadzić pewne zmiany w kodzie, aby działały również w kierunku zgodnym z ruchem wskazówek zegara, proszę podać rozwiązanie, jeśli wiesz.

CGFloat radiusCircle = (self.frame.size.height * 0.5) - ([_lineWidth floatValue]/2.0f); 
-(void)addTargetViewWithOptions:(CGFloat)progress andRadius:(CGFloat)radius{ 

CGFloat x = radius * (1 + (cos(M_PI * (2 * progress + 0.5)))); 
CGFloat y = radius * (1 - (sin(M_PI * (2 * progress + 0.5)))); 
UIView *targetView = [[UIView alloc]initWithFrame:CGRectMake(x, y, 40, 30)]; 
targetView.backgroundColor = [UIColor greenColor]; 
[self addSubview:targetView];} 

Please check my orignal graph

i starałem jak esthepiking wspomniano, tutaj I dodaje kod i zrzut ekranu

-(void)addTargetView{ 

CGFloat endAngle = -90.01f; 
radiusCircle = (self.frame.size.height * 0.5) - ([_lineWidth floatValue]/2.0f); 
endAngleCircle = DEGREES_TO_RADIANS(endAngle);//-1.570971 

// Size for the text 
CGFloat width = 75; 
CGFloat height = 30; 

// Calculate the location of the end of the stroke 
// Cos calculates the x position of the point (in unit coordinates) 
// Sin calculates the y position of the point (in unit coordinates) 
// Then scale this to be on the range [0, 1] to match the view 
CGFloat endX = (cos(endAngleCircle)/2 + 0.5); 
CGFloat endY = (sin(endAngleCircle)/2 + 0.5); 

// Scale the coordinates to match the diameter of the circle 
endX *= radiusCircle * 2; 
endY *= radiusCircle * 2; 

// Translate the coordinates based on the location of the frame 
endX -= self.frame.origin.x; 
endY -= self.frame.origin.y; 

// Vertically align the label 
endY += height; 

// Setup the label 

UIView *targetView = [[UIView alloc]initWithFrame:CGRectMake(endX, endY, width, height)]; 
targetView.backgroundColor = [UIColor redColor]; 
[self addSubview:targetView];} 

Please check this result

+0

convertPoint: toView: powinno załatwić sprawę – Alex

Odpowiedz

5

1. Math

Zapomnijmy o iOS na chwilę i rozwiązać problem matematyczny.

Problem: Znajdź punkt (x; y) dla danego α.

Rozwiązanie: x = cos (α); Y = sin (α)

math

2. Zmiana

Punktem wyjścia jest w 0 ° C w warunkach trygonometrii, lecz raczej π/2. Również kąt łuku jest zdefiniowana przez parametr postępu, a więc prowadzi do tego:

x = cos (postęp * 2 * π + π/2) = -sin (postęp * 2 * π)
y = sin (postęp * 2 * π + π/2) = cos (postęp * 2 * π)

3. teraz wróćmy do iOS:

Ponieważ w iOS pochodzenie jest w górze lewy róg i cofnięta oś y powinieneś przekonwertować poprzednie rozwiązanie w ten sposób:

x '= 0,5 * (1 + x)
Y' = 0,5 * (1 - Y)

zielony dla współrzędnych matematycznych, niebieski dla iOS systemu, które przekształca się w koordynacji :

iOS

teraz wszystko, co musisz zrobić, to wynik należy pomnożyć przez szerokość i wysokość:

x '= 0,5 * szerokość * (1 - sin (postęp * 2 * π))
Y' = 0,5 * wysokość * (1 - cos (postęp * 2 * π))

4 . Kod:

func point(progress: Double, radius: CGFloat) -> CGPoint { 
    let x = radius * (1 - CGFloat(sin(progress * 2 * Double.pi)))) 
    let y = radius * (1 - CGFloat(cos(progress * 2 * Double.pi)))) 
    return CGPoint(x: x, y: y) 
} 

Edit

Jeśli chcesz, aby przełączyć go do ruchu wskazówek zegara, po prostu pomnożyć przez kąt -1:

x = cos (-progress * 2 * π + π/2) = -sin (-progress * 2 * π) = sin (postęp * 2 * π)
y = sin (-progress * 2 * π + π/2) = cos (-progress * 2 * π) = cos (postęp * 2 * π)

x '= 0,5 * szerokość * (1 + sin (progress * 2 * π))
Y”= 0,5 * wysokość * (1 - cos (postęp * 2 * π))

func point(progress: Double, radius: CGFloat) -> CGPoint { 
    let x = radius * (1 + CGFloat(sin(progress * 2 * Double.pi)))) 
    let y = radius * (1 - CGFloat(cos(progress * 2 * Double.pi)))) 
    return CGPoint(x: x, y: y) 
} 

nadzieja pomaga.

+0

Dzięki za odpowiedź! Właściwie pracuję nad wykresem z zegarem w obiektywnym c, ale to nie jest problem. Próbowałem, jak wspomniałeś, uważam, że idealnie działa na wykresie zegara. Musimy wprowadzić pewne zmiany w kodzie, aby działały również w kierunku zgodnym z ruchem wskazówek zegara, proszę podać rozwiązanie, jeśli wiesz. Dodałem mój kod i zrzut ekranu w pierwotnym pytaniu powyżej. – Gopik

+0

To pytanie jest mylące, ponieważ zrzut ekranu nie wygląda jak pasek postępu zgodny z ruchem wskazówek zegara. – alexburtnik

+0

Tak, to był mój błąd. Przepraszam za to! Mógłbyś mieć również mądre rozwiązanie? – Gopik

2

, aby uzyskać punkt, trzeba zrobić trochę trochę matematyki.

Po zdefiniowaniu łuku w ścieżce UIBezierPath, przekazuje się startAngle i endAngle. W tym przypadku, myślę, że chcesz ustawić inny widok z końcem udaru.

UIBezierPath(arcCenter center: CGPoint, radius: CGFloat, startAngle: CGFloat, endAngle: CGFloat, clockwise: Bool) 

Teraz trzeba podjąć sinus i cosinus tego parametru endAngle. Sinus kąta końcowego da ci pozycję y we współrzędnych jednostki, także w zakresie [-1, 1], a cosinus kąta końcowego da ci pozycję x także w współrzędnych jednostki. Współrzędne te mogą być następnie skalowane według rozmiaru łuku i dostosowane tak, aby pasowało do widoku, a następnie przesuwane przez położenie widoku, aby uzyskać położenie końca suwu.

Oto przykładowy plac zabaw dla tego efektu. Wyrównałem UILabel, aby tekst był wycentrowany nad punktem końcowym. Wklej to na plac zabaw iOS, aby wypróbować sam.

Demonstrating the centered view

import UIKit 
import PlaygroundSupport 

PlaygroundPage.current.needsIndefiniteExecution = true 

// Frame to wrap all of this inside of 
let frame = UIView(frame: CGRect(x: 0, y: 0, width: 300, height: 300)) 
frame.backgroundColor = .white 

let radius = 100 as CGFloat 

// Outermost gray circle 
let grayCircle = CAShapeLayer() 
grayCircle.frame = frame.frame 
grayCircle.path = UIBezierPath(arcCenter: frame.center, radius: radius, startAngle: 0, endAngle: CGFloat(M_PI * 2), clockwise: true).cgPath 
grayCircle.strokeColor = UIColor.lightGray.cgColor 
grayCircle.lineWidth = 10 
grayCircle.fillColor = UIColor.clear.cgColor 

frame.layer.addSublayer(grayCircle) 

// Ending angle for the arc 
let endAngle = CGFloat(M_PI/5) 

// Inner green arc 
let greenCircle = CAShapeLayer() 
greenCircle.frame = frame.frame 
greenCircle.path = UIBezierPath(arcCenter: frame.center, radius: radius, startAngle: CGFloat(-M_PI_2), endAngle: endAngle, clockwise: false).cgPath 
greenCircle.strokeColor = UIColor.green.cgColor 
greenCircle.lineWidth = 10 
greenCircle.fillColor = UIColor.clear.cgColor 
greenCircle.lineCap = kCALineCapRound 

frame.layer.addSublayer(greenCircle) 

// Size for the text 
let width = 75 as CGFloat 
let height = 30 as CGFloat 

// Calculate the location of the end of the stroke 
// Cos calculates the x position of the point (in unit coordinates) 
// Sin calculates the y position of the point (in unit coordinates) 
// Then scale this to be on the range [0, 1] to match the view 
var endX = (cos(endAngle)/2 + 0.5) 
var endY = (sin(endAngle)/2 + 0.5) 

// Scale the coordinates to match the diameter of the circle 
endX *= radius * 2 
endY *= radius * 2 

// Translate the coordinates based on the location of the frame 
endX -= frame.frame.origin.x 
endY -= frame.frame.origin.y 

// Vertically align the label 
endY += height 

// Setup the label 
let text = UILabel(frame: CGRect(x: endX, y: endY, width: width, height: height)) 
text.text = "Stroke end!" 
text.font = UIFont.systemFont(ofSize: 14) 
frame.addSubview(text) 

PlaygroundPage.current.liveView = frame 
+0

Dzięki za pomoc. Dodałem mój wynik powyżej. Czy możesz to proszę sprawdzić. – Gopik

+0

@ user1591698 Wygląda na to, że działa, a przynajmniej całkiem blisko. Możliwe, że pozbędziesz się 'endY + = height' dla twojego przypadku użycia. – esthepiking

+0

Tak! Jeszcze raz dziękuję :) – Gopik

Powiązane problemy