Cree el aspecto sangrado que se encuentra en UINavigationBarButton - programáticamente

Estoy tratando de recrear mediante programación el aspecto del botón con sangría que se puede ver en un UINavigationBarButton. No el aspecto brillante de dos tonos o el degradado, solo el sombreado del perímetro:

enter image description here

Parece una sombra oscura interna alrededor de todo el perímetro de la vista, ¿un poco más oscura en la parte superior? Y luego una sombra de resaltado externa alrededor del perímetro de la vista inferior.

Jugué un poco con Core Graphics y experimenté con QuartzCore y sombreado con view.layer.shadowRadius y .shadowOffset, pero ni siquiera puedo hacer que el resaltado inferior se vea bien. Tampoco estoy seguro de por dónde empezar para lograr un sombreado oscuro con compensación interna y un sombreado claro con compensación externa.

preguntado el 12 de junio de 12 a las 09:06

Has mirado Código de pintura? Debería poder recrear el aspecto sangrado usando una sombra interna oscura y una sombra clara. -

No es 'ligeramente más oscuro en la parte superior'; lo más probable es que tenga un desplazamiento vertical (luz desde arriba, distancia de la sombra! = 0 en términos de Photoshop). Parece que el radio de la sombra es de aproximadamente 2 px y la distancia vertical es de aproximadamente 1 px hacia abajo. (por eso 'sangra' en la parte de arriba también) -

2 Respuestas

Parece como si quisieras un borde que se vea como una sombra. Dado que la sombra parece una especie de degradado, no será posible establecer un borde como degradado a primera vista. Sin embargo, es posible crear una ruta que represente el borde y luego rellenarla con un degradado. Apple proporciona lo que parece ser una función poco conocida llamada CGPathCreateCopyByStrokingPath. Esto toma una ruta (digamos, un rectángulo redondeado, por ejemplo) y crea una nueva ruta que sería el trazo de la ruta anterior dada la configuración que pasa a la función (como ancho de línea, configuración de unión/remate, límite de inglete, etc. ). Entonces, digamos que define una ruta (esto no es exactamente lo que proporciona Apple, pero es similar):

+ (UIBezierPath *) bezierPathForBackButtonInRect:(CGRect)rect withRoundingRadius:(CGFloat)radius{
    UIBezierPath *path = [UIBezierPath bezierPath];
    CGPoint mPoint = CGPointMake(CGRectGetMaxX(rect) - radius, rect.origin.y);
    CGPoint ctrlPoint = mPoint;
    [path moveToPoint:mPoint];

    ctrlPoint.y += radius;
    mPoint.x += radius;
    mPoint.y += radius;
    if (radius > 0) [path addArcWithCenter:ctrlPoint radius:radius startAngle:M_PI + M_PI_2 endAngle:0 clockwise:YES];

    mPoint.y = CGRectGetMaxY(rect) - radius;
    [path addLineToPoint:mPoint];

    ctrlPoint = mPoint;
    mPoint.y += radius;
    mPoint.x -= radius;
    ctrlPoint.x -= radius;
    if (radius > 0) [path addArcWithCenter:ctrlPoint radius:radius startAngle:0 endAngle:M_PI_2 clockwise:YES];

    mPoint.x = rect.origin.x + (10.0f);
    [path addLineToPoint:mPoint];

    [path addLineToPoint:CGPointMake(rect.origin.x, CGRectGetMidY(rect))];

    mPoint.y = rect.origin.y;
    [path addLineToPoint:mPoint];

    [path closePath];
    return path;
}

Esto devuelve una ruta similar al botón Atrás de Apple (lo uso en mi aplicación). He agregado este método (junto con docenas más) como una categoría a UIBezierPath.

Ahora agreguemos esa sombra interior en una rutina de dibujo:

- (void) drawRect:(CGRect)rect{
    UIBezierPath *path = [UIBezierPath bezierPathForBackButtonInRect:rect withRoundingRadius:5.0f];
    //Just fill with blue color, do what you want here for the button
    [[UIColor blueColor] setFill]; 
    [path fill];

    [path addClip]; //Not completely necessary, but borders are actually drawn 'around' the path edge, so that half is inside your path, half is outside adding this will ensure the shadow only fills inside the path

    //This strokes the standard path, however you might want to might want to  inset the rect, create a new 'back button path' off the inset rect and create the inner shadow path off that.  
    //The line width of 2.0f will actually show up as 1.0f with the above clip: [path addClip];, due to the fact that borders are drawn around the edge 
    UIBezierPath *innerShadow = [UIBezierPath bezierPathWithCGPath: CGPathCreateCopyByStrokingPath(path.CGPath, NULL, 2.0f, path.lineCapStyle, path.lineJoinStyle, path.miterLimit)];
    //You need this, otherwise the center (inside your path) will also be filled with the gradient, which you don't want
    innerShadow.usesEvenOddFillRule = YES;
    [innerShadow addClip];

    //Now lets fill it with a vertical gradient
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGPoint start = CGPointMake(0, 0);
    CGPoint end = CGPointMake(0, CGRectGetMaxY(rect));
    CGFloat locations[2] = { 0.0f, 1.0f};
    NSArray *colors =  [NSArray arrayWithObjects:(id)[UIColor colorWithWhite:.7f alpha:.5f].CGColor, (id)[UIColor colorWithWhite:.3f alpha:.5f].CGColor, nil];
    CGGradientRef gradRef = CGGradientCreateWithColors(CGColorSpaceCreateDeviceRGB(), (__bridge CFArrayRef)colors, locations);
    CGContextDrawLinearGradient(context, gradRef, start, end, 0);
    CGGradientRelease(gradRef);
}

Ahora bien, esto es sólo un ejemplo simple. No guardo/restauro contextos ni nada, lo que probablemente querrás hacer. Hay cosas que quizás desee hacer para mejorarlo, como insertar la ruta de 'sombra' si desea usar un borde normal. Es posible que desee utilizar más/diferentes colores y ubicaciones. Pero esto debería ayudarte a empezar.

ACTUALIZACIÓN

Hay otro método que puede utilizar para crear este efecto. Escribí un algoritmo para biselar caminos bezier arbitrarios en gráficos centrales. Esto se puede utilizar para crear el efecto que está buscando. Este es un ejemplo de cómo lo uso en mi aplicación:

Botón trasero biselado

Le pasa a la rutina el CGContextRef, CGPathRef, el tamaño del bisel y qué colores quiere que use para el resaltado/sombra.

El código que usé para esto se puede encontrar aquí:Github - Algoritmo de biselado.

También explico el código y mi metodología aquí: Biselado-Formas en Core Graphics

Respondido el 21 de junio de 12 a las 21:06

Usar la sombra de la capa no lo hará. Necesitas una sombra exterior clara y una sombra interior oscura para conseguir ese efecto. Una capa solo puede tener una sombra (exterior). (Además, las sombras de las capas se redibujan dinámicamente y fuerzan la representación basada en CPU, lo que reduce el rendimiento).

Deberá hacer su propio dibujo con CoreGraphics, ya sea en una vista drawRect: método o una capa drawInContext: método. (O dibuja en un contexto de imagen y luego reutiliza la imagen). Dicho dibujo utilizará principalmente CGContext functions. (Nombraré algunos a continuación, pero este enlace tiene documentación para todos ellos).

Para un botón recto redondo, puede que le resulte tedioso crear el CGPath apropiado; en su lugar, puede usar +[UIBezierPath bezierPathWithRoundedRect:cornerRadius:] y luego el camino CGPath propiedad para establecer la ruta actual del contexto con CGContextAddPath.

Puede crear una sombra interior estableciendo un trazado de recorte (consulte CGContextClip y funciones relacionadas) a la forma del botón, configurando una sombra (ver CGContextSetShadowWithColor y funciones relacionadas), y luego dibujar alrededor del exterior de la forma que desea sombrear. Para la sombra interior, trazo (CGContextStrokePath) un rectángulo redondo que es un poco más grande que su botón, usando un ancho de trazo grueso (CGContextSetLineWidth) por lo que hay mucha "tinta" para generar una sombra (recuerde, este trazo no será visible debido a la ruta de recorte).

Puede crear una sombra exterior de la misma manera: no use un trazado de recorte esta vez, porque desea que la sombra esté fuera de la forma y rellene (CGContextFillPath) la forma de su botón en lugar de acariciarlo. Tenga en cuenta que dibujar una sombra es una especie de "modo": guarda el estado de los gráficos (CGContextSaveGState), configure una sombra, luego dibuje la forma de la que desea ver una sombra (la forma en sí no se dibuja cuando está en este modo) y finalmente restaure el estado (CGContextRestoreGState) para salir del "modo sombra". Dado que ese modo no dibuja la forma, solo la sombra, deberá dibujar la forma por separado.

También hay una orden para hacer todo esto. Debería ser obvio si piensa en el orden en que pintaría estas cosas con medios físicos: primero dibuje la sombra exterior, luego el relleno del botón, luego la sombra interior. Puede agregar un trazo después de eso si la sombra interna no le da un contorno lo suficientemente pronunciado.


Hay algunas herramientas de dibujo que pueden generar código fuente para CoreGraphics: Opacidad es uno que yo uso. Sin embargo, tenga cuidado con estos, ya que el código que generan puede no ser eficiente.

Respondido el 15 de junio de 12 a las 08:06

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.