Rysowanie ścieżki UIBezierPath na wygenerowanym kodzie UIView
Mam UIView
dodane w kodzie w czasie wykonywania.
Chcę w nim narysować UIBezierPath
, ale czy to oznacza, że muszę nadpisać drawRect
dla UIView?
A może jest inny sposób rysowania na zamówienie UIView
?
Oto kod do generowania UIView
:
UIView* shapeView = [[UIView alloc]initWithFrame:CGRectMake(xOrigin,yOrigin+(i*MENU_BLOCK_FRAME_HEIGHT), self.shapeScroll.frame.size.width, MENU_BLOCK_FRAME_HEIGHT)];
shapeView.clipsToBounds = YES;
A oto funkcja do tworzenia i zwracania UIBezierPath
:
- (UIBezierPath*)createPath
{
UIBezierPath* path = [[UIBezierPath alloc]init];
[path moveToPoint:CGPointMake(100.0, 50.0)];
[path addLineToPoint:CGPointMake(200.0,50.0)];
[path addLineToPoint:CGPointMake(200.0, 200.0)];
[path addLineToPoint:CGPointMake(100.0, 200.0)];
[path closePath];
return path;
}
6 answers
Nie tak dawno temu nawet nie wiedziałem, jak wymawiać Béziera, nie mówiąc już o tym, jak używać ścieżek Béziera do tworzenia niestandardowych kształtów. Oto, czego się nauczyłem. Okazuje się, że nie są tak przerażające, jak się na początku wydają.
Jak narysować ścieżkę Béziera w widoku niestandardowym
Oto główne kroki:
- Zaprojektuj zarys pożądanego kształtu.
- podziel ścieżkę konturu na segmenty linii, łuków i krzywe.
- Zbuduj tę ścieżkę programowo.
- Narysuj ścieżkę w
drawRect
lub używającCAShapeLayer
.
Design shape outline
Możesz zrobić wszystko, ale jako przykład wybrałem poniższy kształt. To może być wyskakujący klawisz na klawiaturze.
Podziel ścieżkę na segmenty
Spójrz wstecz na swój kształt i podziel go na prostsze elementy linii (dla linii prostych), łuków (dla okręgów i okrągłych narożniki), i krzywe (dla czegokolwiek innego).
Oto jak wyglądałby nasz przykładowy projekt:]}- czarne są segmentami linii
- Jasnoniebieski to segmenty łukowe
- czerwone są krzywe
- pomarańczowe kropki są punktami kontrolnymi krzywych
- zielone kropki są punktami między segmentami ścieżki
- kropkowane linie pokazują obwiedniowy prostokąt
- ciemnoniebieskie liczby są segmentami w kolejności, w jakiej będą być dodawane programowo
Zbuduj ścieżkę programowo
Dowolnie zaczniemy w lewym dolnym rogu i będziemy pracować zgodnie z ruchem wskazówek zegara. Użyję siatki na obrazku, aby uzyskać wartości x i y dla punktów. Zakoduję wszystko tutaj, ale oczywiście nie zrobiłbyś tego w prawdziwym projekcie.
Podstawowy proces to:
- Utwórz nowy
UIBezierPath
- Wybierz punkt początkowy na ścieżce z
moveToPoint
- Dodaj segmenty do ścieżka
- linia:
addLineToPoint
- arc:
addArcWithCenter
- krzywa:
addCurveToPoint
- linia:
- Zamknij ścieżkę za pomocą
closePath
Oto kod do utworzenia ścieżki na powyższym obrazku.
func createBezierPath() -> UIBezierPath {
// create a new path
let path = UIBezierPath()
// starting point for the path (bottom left)
path.move(to: CGPoint(x: 2, y: 26))
// *********************
// ***** Left side *****
// *********************
// segment 1: line
path.addLine(to: CGPoint(x: 2, y: 15))
// segment 2: curve
path.addCurve(to: CGPoint(x: 0, y: 12), // ending point
controlPoint1: CGPoint(x: 2, y: 14),
controlPoint2: CGPoint(x: 0, y: 14))
// segment 3: line
path.addLine(to: CGPoint(x: 0, y: 2))
// *********************
// ****** Top side *****
// *********************
// segment 4: arc
path.addArc(withCenter: CGPoint(x: 2, y: 2), // center point of circle
radius: 2, // this will make it meet our path line
startAngle: CGFloat(M_PI), // π radians = 180 degrees = straight left
endAngle: CGFloat(3*M_PI_2), // 3π/2 radians = 270 degrees = straight up
clockwise: true) // startAngle to endAngle goes in a clockwise direction
// segment 5: line
path.addLine(to: CGPoint(x: 8, y: 0))
// segment 6: arc
path.addArc(withCenter: CGPoint(x: 8, y: 2),
radius: 2,
startAngle: CGFloat(3*M_PI_2), // straight up
endAngle: CGFloat(0), // 0 radians = straight right
clockwise: true)
// *********************
// ***** Right side ****
// *********************
// segment 7: line
path.addLine(to: CGPoint(x: 10, y: 12))
// segment 8: curve
path.addCurve(to: CGPoint(x: 8, y: 15), // ending point
controlPoint1: CGPoint(x: 10, y: 14),
controlPoint2: CGPoint(x: 8, y: 14))
// segment 9: line
path.addLine(to: CGPoint(x: 8, y: 26))
// *********************
// **** Bottom side ****
// *********************
// segment 10: line
path.close() // draws the final line to close the path
return path
}
Uwaga: część powyższego kodu można zredukować poprzez dodanie linii i łuku w jednym poleceniu (ponieważ łuk ma domniemany punkt początkowy). Zobacz tutaj aby uzyskać więcej szczegółów.
Narysuj ścieżkę
Możemy narysować ścieżkę w warstwie lub w drawRect
.
Metoda 1: rysowanie ścieżki w warstwie
Nasza klasa niestandardowa wygląda tak. Dodajemy naszą ścieżkę Beziera do nowejCAShapeLayer
, Gdy widok jest inicjowany.
import UIKit
class MyCustomView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
func setup() {
// Create a CAShapeLayer
let shapeLayer = CAShapeLayer()
// The Bezier path that we made needs to be converted to
// a CGPath before it can be used on a layer.
shapeLayer.path = createBezierPath().cgPath
// apply other properties related to the path
shapeLayer.strokeColor = UIColor.blue.cgColor
shapeLayer.fillColor = UIColor.white.cgColor
shapeLayer.lineWidth = 1.0
shapeLayer.position = CGPoint(x: 10, y: 10)
// add the new layer to our custom view
self.layer.addSublayer(shapeLayer)
}
func createBezierPath() -> UIBezierPath {
// see previous code for creating the Bezier path
}
}
I tworzenie naszego widoku w kontrolerze widoku w ten sposób
override func viewDidLoad() {
super.viewDidLoad()
// create a new UIView and add it to the view controller
let myView = MyCustomView()
myView.frame = CGRect(x: 100, y: 100, width: 50, height: 50)
myView.backgroundColor = UIColor.yellow
view.addSubview(myView)
}
Dostajemy...
Hmm, to trochę za małe, bo zakodowałem wszystkie liczby. Mogę jednak przeskalować rozmiar ścieżki, jak to:let path = createBezierPath()
let scale = CGAffineTransform(scaleX: 2, y: 2)
path.apply(scale)
shapeLayer.path = path.cgPath
Metoda 2: Narysuj ścieżkę w draw
Używanie draw
jest wolniejsze niż rysowanie na warstwie, więc nie jest to zalecana metoda, jeśli jej nie potrzebujesz.
Oto poprawiony kod dla naszego niestandardowego widoku:
import UIKit
class MyCustomView: UIView {
override func draw(_ rect: CGRect) {
// create path (see previous code)
let path = createBezierPath()
// fill
let fillColor = UIColor.white
fillColor.setFill()
// stroke
path.lineWidth = 1.0
let strokeColor = UIColor.blue
strokeColor.setStroke()
// Move the path to a new location
path.apply(CGAffineTransform(translationX: 10, y: 10))
// fill and stroke the path (always do these last)
path.fill()
path.stroke()
}
func createBezierPath() -> UIBezierPath {
// see previous code for creating the Bezier path
}
}
Co daje nam ten sam wynik...
Dalsze badania
I naprawdę zalecam zapoznanie się z poniższymi materiałami. Są tym, co w końcu udało mi się zrozumieć ścieżki Béziera. (I nauczył mnie jak to wymawiać: / ˈbɛ zi eɪ/.)
- myślenie jak Ścieżka Béziera (Wszystko, co kiedykolwiek przeczytałem od tego autora, jest dobre i inspiracja dla mojego przykładu powyżej pochodziła stąd.)
- Coding Math: Odcinek 19 - krzywe Beziera (zabawne i dobre ilustracje wizualne)
- Krzywe Beziera (w jaki sposób są wykorzystywane w aplikacjach graficznych)
- Bezier Krzywe (dobry opis wyprowadzania formuł matematycznych)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-14 01:39:20
Byłoby łatwiej, gdybyś użył CAShapeLayer
, w ten sposób:
CAShapeLayer *shapeView = [[CAShapeLayer alloc] init];
I ustaw jego path
:
[shapeView setPath:[self createPath].CGPath];
Na koniec dodaj:
[[self.view layer] addSublayer:shapeView];
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-23 15:14:20
Możesz użyć CAShapeLayer
, Aby to zrobić.
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
shapeLayer.path = [self createPath].CGPath;
shapeLayer.strokeColor = [UIColor redColor].CGColor; //etc...
shapeLayer.lineWidth = 2.0; //etc...
shapeLayer.position = CGPointMake(100, 100); //etc...
[self.layer addSublayer:shapeLayer];
Spowoduje to dodanie i narysowanie ścieżki bez konieczności nadpisywania drawRect
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-22 00:58:02
Istnieje wiele sposobów, aby osiągnąć to, co chcesz. Te, które widziałem najbardziej to: override drawRect, narysuj swój kształt w CAShapeLayer, a następnie dodaj go jako podwarstwę do widoku, lub Narysuj swoją ścieżkę w innym kontekście, zapisz to jako obraz, a następnie dodaj go do widoku.
Wszystkie z nich są rozsądnymi wyborami, a to, który z nich jest najlepszy, zależy od wielu innych czynników, takich jak: czy będziesz stale dodawać kształty, jak często to się nazywa itp.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:18:27
Jak zauważyły inne plakaty, użycie warstwy kształtu jest dobrym sposobem.
Warstwy kształtu a prawdopodobnie zapewnią lepszą wydajność niż zastąpienie funkcji drawRect.
Jeśli chcesz samodzielnie narysować ścieżkę, to tak, musisz nadpisać drawRect dla swojej niestandardowej klasy widoku.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-23 15:18:06
Tak, musisz nadpisać drawrect, jeśli chcesz coś narysować.Tworzenie ścieżki UIBezierPath można wykonać w dowolnym miejscu, ale aby coś narysować należy to zrobić wewnątrz metody drawrect
Powinieneś wywoływać setNeedsDisplay
, jeśli nadpisujesz drawRect w podklasie UIView, która jest w zasadzie niestandardowym widokiem rysującym coś na ekranie, jak linie, obraz , prostokąt.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-23 15:22:40