2011-02-24 29 views
35

Próbuję narysować koło kolorów dla iPhone, ale nie mogę uzyskać gradientu obrócić wokół punktu. Ja próbuje użyć gradienty ale celem-C uzyskuje się gradient liniowy, który opiera się na gradient prostą linię: linear gradientJak narysować koło kolorów w obiektyw-c

i promieniowego gradientu, który wciąga gradientem zaczynając w punkcie promieniujący się we wszystkich kierunkach tak: radial gradient

Chcę narysować gradient liniowy, który obraca się wokół punktu jak ten:

colorwheel

+1

Wysłany to dzisiaj http://code.google.com/p/ios-color-wheel/ –

+0

Niestety, aby przywołać starą pytanie, ale podczas importowania biblioteki w moim projekcie zawsze otrzymują błąd dotyczący "Mach-O-Linker" z następującej linii: MainViewController * mainViewController = [[MainViewController alloc] init]; – CHRIS

+0

Lepiej przewartościować, jak zły jest interfejs użytkownika, który jest dla wskaźnika tak duży, jak dotyk. Znacznie lepiej rozważyć próbki, ewentualnie w drzewie. Ale dla prawdziwej precyzji użyj suwaków komponentów przestrzeni kolorów i próbki podglądu. – uchuugaka

Odpowiedz

23

Następujące rysuje koło kolorów HSL w podklasie UIView. Czyni to, generując bitmapę, obliczając dla każdego piksela poprawną wartość koloru. Nie jest to dokładnie to, co próbujesz zrobić (wygląda na to, że tylko odcień różni się w kole ze stałą luminancją/nasyceniem), ale powinieneś być w stanie dostosować go do swoich potrzeb.

Należy pamiętać, że może nie mieć optymalnej wydajności, ale powinno zacząć. Możesz także użyć getColorWheelValue() do obsługi danych wprowadzanych przez użytkownika (kliknięć/dotknięć przy danej współrzędnej).

- (void)drawRect:(CGRect)rect 
{ 
    int dim = self.bounds.size.width; // should always be square. 
    bitmapData = CFDataCreateMutable(NULL, 0); 
    CFDataSetLength(bitmapData, dim * dim * 4); 
    generateColorWheelBitmap(CFDataGetMutableBytePtr(bitmapData), dim, luminance); 
    UIImage *image = createUIImageWithRGBAData(bitmapData, self.bounds.size.width, self.bounds.size.height); 
    CFRelease(bitmapData); 
    [image drawAtPoint:CGPointZero]; 
    [image release]; 
} 

void generateColorWheelBitmap(UInt8 *bitmap, int widthHeight, float l) 
{ 
    // I think maybe you can do 1/3 of the pie, then do something smart to generate the other two parts, but for now we'll brute force it. 
    for (int y = 0; y < widthHeight; y++) 
    { 
     for (int x = 0; x < widthHeight; x++) 
     { 
      float h, s, r, g, b, a; 
      getColorWheelValue(widthHeight, x, y, &h, &s); 
      if (s < 1.0) 
      { 
       // Antialias the edge of the circle. 
       if (s > 0.99) a = (1.0 - s) * 100; 
       else a = 1.0; 

       HSL2RGB(h, s, l, &r, &g, &b); 
      } 
      else 
      { 
       r = g = b = a = 0.0f; 
      } 

      int i = 4 * (x + y * widthHeight); 
      bitmap[i] = r * 0xff; 
      bitmap[i+1] = g * 0xff; 
      bitmap[i+2] = b * 0xff; 
      bitmap[i+3] = a * 0xff; 
     } 
    } 
} 

void getColorWheelValue(int widthHeight, int x, int y, float *outH, float *outS) 
{ 
    int c = widthHeight/2; 
    float dx = (float)(x - c)/c; 
    float dy = (float)(y - c)/c; 
    float d = sqrtf((float)(dx*dx + dy*dy)); 
    *outS = d; 
    *outH = acosf((float)dx/d)/M_PI/2.0f; 
    if (dy < 0) *outH = 1.0 - *outH; 
} 

UIImage *createUIImageWithRGBAData(CFDataRef data, int width, int height) 
{ 
    CGDataProviderRef dataProvider = CGDataProviderCreateWithCFData(data); 
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); 
    CGImageRef imageRef = CGImageCreate(width, height, 8, 32, width * 4, colorSpace, kCGImageAlphaLast, dataProvider, NULL, 0, kCGRenderingIntentDefault); 
    UIImage *image = [[UIImage alloc] initWithCGImage:imageRef]; 
    CGDataProviderRelease(dataProvider); 
    CGColorSpaceRelease(colorSpace); 
    CGImageRelease(imageRef); 
    return image; 
} 

// Adapted from Apple sample code. See http://en.wikipedia.org/wiki/HSV_color_space#Comparison_of_HSL_and_HSV 
void HSL2RGB(float h, float s, float l, float* outR, float* outG, float* outB) 
{ 
    float temp1, temp2; 
    float temp[3]; 
    int i; 

    // Check for saturation. If there isn't any just return the luminance value for each, which results in gray. 
    if(s == 0.0) 
    { 
     *outR = l; 
     *outG = l; 
     *outB = l; 
     return; 
    } 

    // Test for luminance and compute temporary values based on luminance and saturation 
    if(l < 0.5) 
     temp2 = l * (1.0 + s); 
    else 
     temp2 = l + s - l * s; 
    temp1 = 2.0 * l - temp2; 

    // Compute intermediate values based on hue 
    temp[0] = h + 1.0/3.0; 
    temp[1] = h; 
    temp[2] = h - 1.0/3.0; 

    for(i = 0; i < 3; ++i) 
    { 
     // Adjust the range 
     if(temp[i] < 0.0) 
      temp[i] += 1.0; 
     if(temp[i] > 1.0) 
      temp[i] -= 1.0; 


     if(6.0 * temp[i] < 1.0) 
      temp[i] = temp1 + (temp2 - temp1) * 6.0 * temp[i]; 
     else { 
      if(2.0 * temp[i] < 1.0) 
       temp[i] = temp2; 
      else { 
       if(3.0 * temp[i] < 2.0) 
        temp[i] = temp1 + (temp2 - temp1) * ((2.0/3.0) - temp[i]) * 6.0; 
       else 
        temp[i] = temp1; 
      } 
     } 
    } 

    // Assign temporary values to R, G, B 
    *outR = temp[0]; 
    *outG = temp[1]; 
    *outB = temp[2]; 
} 
+0

Straty mocy baterii, aby wielokrotnie to obliczać. Ładne kodowanie, ale nie tylko świetna bateria użytkownika. – uchuugaka

+0

@uchuugaka zostanie to wywołane tylko raz, gdy widok zostanie narysowany, a następnie tylko wtedy, gdy wywołany zostanie 'setNeedsDisplay'. –

+0

Właściwie, gdy wyświetlany jest ekran. setNeedsDisplay po prostu ustawia flagę, główna pętla idzie, a połączenia wyświetlają się na wszystkim, co jest potrzebne, a wszystko w środku. (może również zostać wywołany w ramach układu) – uchuugaka

1

nie jestem świadomy wszystkiego, co zrobi to za yo u. Możesz to przybliżyć przez drawing triangles, które pochodzą z (żółty-> czerwony, czerwony-> purp, purp-> niebieski, itp.), A następnie umieszczenie circle mask over it. Spójrz na CGGradient.

Przykro mi, ale nie mogę pomóc.

4

Zrobiłem coś takiego, tworząc dużą bitmapę RGBA, kolorując każdy piksel w zależności od jego lokalizacji, przekształcony na współrzędne biegunowe, a następnie przekształcając bitmapę w obraz i rysując obraz w powiększeniu. Skalowanie miało pomóc pikselowaniu antialias w pobliżu centrum.

2

Będziesz musiał zrobić bitmap zgodnie z sugestią hotpaw2. Najbardziej skutecznym sposobem obliczenia tego jest użycie HSL color space. Umożliwi to utworzenie funkcji, która przyjmuje jako dane wejściowe lokalizację piksela i wyprowadza wartość RGB, używając tylko kilku podstawowych operacji arytmetycznych i małego triggla do obliczenia.

Jeśli chcesz dowolne kolory wzdłuż "szprychy" koła, cóż, to wymaga więcej pracy. Musisz obliczyć wartości mieszania dla każdego piksela i wymaga to wyraźniejszego widzenia, więcej trygonometrii i nie jest tak szybki. Kiedy to zrobiłem, musiałem je wektoryzować i zrównoleglować, aby usunąć niewielkie (choć wyczuwalne) opóźnienie odświeżania z ponownego obliczenia bitmapy - na MacBooku Pro. Na iPhonie nie masz tych opcji, więc będziesz musiał żyć z opóźnieniem.

Daj mi znać, jeśli potrzebujesz bardziej szczegółowych wyjaśnień tych technik; Będę szczęśliwy, mogąc to zrobić.

17

używając jedynie metody UIKit:

// ViewController.m; assuming ViewController is the app's root view controller 
#include "ViewController.h" 
@interface ViewController() 
{ 
    UIImage *img; 
    UIImageView *iv; 
} 
@end 

@implementation ViewController 
- (void)viewDidLoad 
{ 
    [super viewDidLoad]; 

    CGSize size = CGSizeMake(self.view.bounds.size.width, self.view.bounds.size.height); 
    UIGraphicsBeginImageContextWithOptions(CGSizeMake(size.width, size.height), YES, 0.0); 
    [[UIColor whiteColor] setFill]; 
    UIRectFill(CGRectMake(0, 0, size.width, size.height)); 

    int sectors = 180; 
    float radius = MIN(size.width, size.height)/2; 
    float angle = 2 * M_PI/sectors; 
    UIBezierPath *bezierPath; 
    for (int i = 0; i < sectors; i++) 
    { 
     CGPoint center = CGPointMake(size.width/2, size.height/2); 
     bezierPath = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:i * angle endAngle:(i + 1) * angle clockwise:YES]; 
     [bezierPath addLineToPoint:center]; 
     [bezierPath closePath]; 
     UIColor *color = [UIColor colorWithHue:((float)i)/sectors saturation:1. brightness:1. alpha:1]; 
     [color setFill]; 
     [color setStroke]; 
     [bezierPath fill]; 
     [bezierPath stroke]; 
    } 
    img = UIGraphicsGetImageFromCurrentImageContext(); 
    UIGraphicsEndImageContext(); 

    iv = [[UIImageView alloc] initWithImage:img]; 
    [self.view addSubview:iv]; 
} 
@end 

zasadzie wszystko, że powyższy kod robi to obejść kręgu rysunku wąskie sektory wypełniając je z kolorami stopniowo rosnącej odcień.

Można oczywiście wykonać cały rysunek bezpośrednio w kontekście graficznym widoku z drawRect() i nie trzeba tworzyć wyraźnego kontekstu obrazu.

enter image description here