Przegląd możliwości tworzenia grafiki i animacji w iOS. Wydaje się, że większość czego potrzebujemy jest. Funkcjonalnymi - niekiedy bardziej niskopoziomowymi - odpowiednikami XAML są UIKit i Core Graphics, w tym Quartz 2D. Odpowiednikiem DirectX jest OpenGL ES. Kilka - tym razem - drobnych uwag:
- duża cześć bibliotek m.in Core Graphics i OpenGL ES oferuje API w C, aczkolwiek nie wydaje się to dużym problemem
- trochę przekombinowane rysowanie podstawowych kształtów za pomocą API do krzywych Beziera w UIKit
- ciekawostką jest API w UIKit pozwalające szybko zrobić animację z gotowej sekwencji obrazków
- API w UIKit do animacji nie wydaje się tak uniwersalne, panuje pewien śmietnik we flagach, brakuje uniwersalnej obsługi easing functions
- Quartz 2D wydaje się takim nieco szerszym odpowiednikiem canvas w HTML5, w tej niższej warstwie znajdziemy też dużo rzeczy znanych z XAML np. gradienty, clipping, transformacje
- aplikacje uniwersalne w iOS siłą rzeczy kojarzą mi się z aplikacjami uniwersalnymi na Windows i Windows Phone, przy czym iOS jest jeden
Wprowadzenie
UIKit
- aplikacje biznesowe
- UIImage
- UIImageView
- UIBezierPath
- UIColor
UIImage *img = [UIImage imageNamed: @”plik.png”]
UIImageView *iv = [ [UIImageView alloc] initWithImage: img];
iv.frame = CGRectMake(50, 25, iv.frame.size.width * 2, iv.frame.size.height * 2); //przesunięcie i skalowanie
Core Graphics
- w C (nie w Objective-C)
- CGContextRef - opakowanie wywołania kodu w C
- CGImageRef - operacje na pikselach obrazka
- CGAffineTransform - transformaty obrazków (skalowanie, skrzywianie, obrót)
- funkcje do tworzenia dokumentów PDF
// klasa dziedzicząca po UIView:
- (id) initWithFrame: (CGRect) frame {
self = [super initWithFrame: frame];
if (self) {
}
return self;
}
- (void) drawRect: (CGRect) rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [UIColor redColor].CGColor);
CGContextFillRect(context, CGRectMake(40, 40, 100, 200));
}
Quartz 2D
- używa większość obiektów z Core Graphics np. CGContextRef
- CGContextBeginPath(…)
- CGContextMoveToPoint(…)
- CGContextAddLineToPath(…), CGContextAddCurveToPoint(…)
- CGContextFillPath(…)
- CGContextStrokePath(…)
Core Animation
- UIKit jest zbudowany na Core Animation
- warstwy
- wydajne biblioteki animacji
OpenGL ES
- najszybsze renderowanie 2d i 3d
- EAGLContext
- frameworki
- Sprite Kit
- Unity 3D
- open source
Sprite Kit
- klocki
- iOS 7
- engine gier 2D
- akceleracja sprzętowa
- Objective-C
UIKit
Testowe załadowanie obrazka z sieci:
NSURL *url = [NSURL URLWithString: @”http://…”];
NSData *data = [NSData dataWithContentsOfURL: url];
UIImage *img = [[UIImage alloc] initWithData: data];
//produkcyjnie typowy asynchroniczny request
Krzywe i ścieżki Beziera:
UIBezierPath *path = [UIBezierPath bezierPathWithRect: CGRectMake(10, 10, 100, 100)]; //prostokąt
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect: CGRectMake(50, 50, 100, 100)]; //okrąg
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(10, 10, 100, 100)
cornerRadius: 20.0] // prostokąt z zaokrąglonymi rogami
UIBezierPath *path = [UIBezierPath bezierPath]; //ścieżka Beziera definiowana w oparciu o punkty
[[UIColor redColor] setFill];
[[UIColor greenColor] setStroke];
path.lineWidth = 4;
[path moveToPoint: CGPointMake(100, 100)]; //trójkąt
[path addLineToPoint: CGPointMake(200, 100)];
[path addLineToPoint: CGPointMake(200, 300)];
//dodanie krzywej
//[path addCurveToPoint: CGPointMake(0, 300) controlPoint1: CGPointMake(150, 400)
// controlPoint2: CGPointMake(50, 200)];
[path addQuadCurveToPoint: CGPointMake(0, 300) controlPoint: CGPointMake(100, 400)];
[path closePath];
[path fill];
[path stroke];
Animacja ramkowa:
//animacja z wielu obrazków
UIImageView *iv = [ [UIImageView alloc] initWithFrame: CGRectMake(40, 40, 95, 70)];
…
iv.animationImages = array; //tablica obrazków UIImage
iv.animationDuration = 1.0 / 2;
[self.view addSubview: iv];
[iv startAnimating];
Animacja proceduralna:
//zmiana propercji
iv.alpha = 0;
[self.view addSubview: iv];
[UIView animateWithDuration: 5.0
animations: ^ {
iv.alpha = 1.0;
iv.frame = CGRectMake(100, 100, 95*2, 70*2);
iv.transform = CGAffineTransformMakeRotation(M_PI);
}];
Więcej parametrów dla animacji:
animateWithDuration: delay: options: animations: completion:
enum UIViewAnimationOptions – m.in pozwolenie po interakcji użytkownika, powtarzanie, autorewers, niektóre funkcje przebiegu, pewne obrócenia
[UIView animateWithDuration: 5.0
delay: 0
options: UIViewAnimationOptionAutoreverse | UIViewAnimationOptionRepeat
animations: ^ {
iv.alpha = 1.0;
iv.frame = CGRectMake(100, 100, 95*2, 70*2);
iv.transform = CGAffineTransformMakeRotation(M_PI);
},
completion: ^(BOOL finished) {
//np. można ustawić coś po zakończeniu lub odpalić inną animację
}
];
Quartz 2D
Rysowanie kształtów:
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [UIColor redColor].CGColor);
CGContextSetStrokeColorWithColor(context, [UIColor greenColor].CGColor);
CGContextSetLineWidth(context, 4);
CGContextFillRect(context, CGRectMake(40, 40, 100, 200)); //prostokąt
CGContextStrokeRect(context, CGRectMake(30, 30, 100, 300));
CGContextFillEllipseInRect(context, CGRectMake(30, 30, 100, 300)); //elipsa
CGContextStrokeEllipseInRect(context, CGRectMake(30, 30, 100, 300));
CGContextBeginPath(context); //inne
CGContextMoveToPoint(context, 30, 30);
CGContextAddLineToPoint(context, 300, 30);
CGContextAddLineToPoint(context, 300, 300);
CGContextAddCurveToPoint(context, 250, 350, 50, 250, 30, 300); //2 punkty kontrolne i punkt docelowy
CGContextClosePath(context);
CGContextDrawPath(context, kCGPathFillStroke /* kCGPathStroke */);
//wycinek koła
CGContextBeginPath(context);
CGContextMoveToPoint(context, x, y);
CGContextAddArc(context, x, y, rad, startAngle, endAngle, 0);
CGContextClosePath(context);
Kolory:
predefiniowane:
[UIColor redColor]
RGB:
[UIColor colorWithRed: 55.0/255.0 green: 0.5 blue: 0.5 alpha: 1.0]
CGFloat components[] = {0, 0, 1, 1};
CGColorRef c = CGColorCreate (CGColorSpaceCreateDeviceRGB(), components);
CGContextSetFillColorWithColor(context, c);
…
CGColorRelease(c);
Zapisanie kontekstu przed zmianą i przywrócenie po wykonaniu czynności:
CGContextSaveCGState(context);
CGContextSetFillColorWithColor(context, [UIColor blueColor].CGColor);
CGContextFillEllipseInRect(context, CGRectMake(30, 30, 100, 300));
CGContextRestoreGState(context);
Gradient liniowy:
CGFloat colors[] = {
1.0, 0.0, 0.0, 1.0,
0.0, 0.0, 1.0, 1.0
};
CGGradientRef gradient = CGGradientCreateWithColorComponents(CGColorSpaceCreateDeviceRGB(), colors, NULL, 2);
CGPoint startPoint = CGPointMake(0, CGRectGetMinY(rect));
CGPoint endPoint = CGPointMake(0, CGRectGetMaxY(rect));
CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0);
// kCGGradientDrawsAterEndLocation | kCGGradientDrawsBeforeStartLocation
CGGradientRelease(gradient);
Clipping:
CGRect r = CGRectMake(50, 50, 200, 200);
CGContextClipToRect(context, r);
Memory leaks:
Project –> Analyze
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, colors, NULL, 2);
CGColorSpaceRelease(colorSpace);
Gradient radialny:
CGPoint gradCenter = CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect));
float gradRadius = rect.size.width/2;
CGContextDrawRadialGradient(context, gradient, gradCenter, 0 /* wycięta w środku przestrzeń*/, gradCenter, gradRadius,
kCGGradientDrawsAfterEndLocation);
Tekst:
CGContextSetFillColorWithColor(context, [UIColor redColor].CGColor);
NSString *text = @”xxx”;
[text drawAtPoint: CGPointMake(40, 40) withFont: [UIFont fontWithName: @”Helvetica” size: 40]];
//bardziej pracochłonna alternatywa
CGAffineTransform xform = CGAffineTransformMake(1.0, 0.0, 0.0, –1.0, 0.0, 0.0);
CGContextSetTextMatrix(context, xform);
CGContextSelectFont(context, “Helvetica”, 40, kCGEncodingMacRoman);
//CGContextSetTextDrawingMode(context, kCGTextFill);
CGContextShowTextAtPoint(context, 0, 30, “xxx” /* ASCII */, 7);
Bitmapy:
UIImage *img = [UIImage imageNamed: @”xxx”];
[img drawAtPoint: CGPointMake(40, 40)];
//bardziej pracochłonna alternatywa
CGContextTranslateCTM(context, 0, img.size.height + 80);
CGContextScaleCTM(context, 1,0, -1.0);
CGContextDrawImage(context, CGRectMake(40, 40, 95, 70), img.CGImage);
Transformacje:
CGContextTranslate(context, 320/2, 480/2);
CGContextScaleCTM(context, 3, 3);
CGContextRotateCTM(context, M_PI/4);
Różne ekrany i urządzenia
Aplikacje uniwersalne – na iPhone i iPad
Różnicowanie zasobów
- MyImage-ipad.png
- MyImage-iphone.png
Uzupełnienia
Animacja z timerem:
NSTimer *timer;
…
timer = [NSTimer scheduledTimerWithTimeInterval: 1/30.0 target: self selector: @selector(nextFrame)
userInfo: NULL repeats: YES];
- (void) nextFrame {
//zmiana stanu, może być sterowana przez easing function
}
Wymuszenie przerysowania:
[myView setNeedsDisplay];
Easing functions:
Znalezienie punktu dotyku w obiekcie wizualnym:
- (void) touchesBegan: (NSSet *) touches withEvent: (UIEvent *)event {
UITouch *t = [touches anyObject];
CGPoint p = [t locationInView: myView];
}
Funkcja trygonometryczna:
atan2(deltaY, deltaX);
Czas:
double time = CACurrentMediaTime();
Brak komentarzy:
Prześlij komentarz