Cree el aspecto con sangría encontrado en UINavigationBarButton – programáticamente

Estoy intentando recrear mediante progtwigción el aspecto del button con sangría que se puede ver en un UINavigationBarButton. No es el aspecto shiny de dos tonos o el gradiente, solo el sombreado del perímetro:

introduzca la descripción de la imagen aquí

Parece un sombreado oscuro interno alnetworkingedor de todo el perímetro de vista, un poco más oscuro en la parte superior? Y luego una sombra destacada externa alnetworkingedor del perímetro de la vista inferior.

He jugado un poco con Core Graphics, y he experimentado con QuartzCore y shadowing con view.layer.shadowRadius y .shadowOffset, pero ni siquiera puedo hacer que el resaltado inferior se vea bien. Tampoco estoy seguro de dónde empezar a lograr tanto un sombreado oscuro con desplazamiento interno como un sombreado ligero con desplazamiento externo.

Parece que quieres un borde que luzca como una sombra. Dado que la sombra aparece en algún tipo de degradado, a primera vista no será posible establecer un borde como degradado. Sin embargo, es posible crear una ruta que represente el borde y luego llenarla con un gradiente. Apple proporciona lo que parece ser una function poco conocida llamada CGPathCreateCopyByStrokingPath . Esto toma una ruta (por ejemplo, un rectángulo networkingondeado, por ejemplo) y crea una nueva ruta que sería el trazo de la ruta anterior dada la configuration que pasas a la function (como ancho de línea, ajuste de unión / límite, límite de ingletes, etc. ) 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 button 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 agregue esa sombra interna 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 esto es solo un simple ejemplo. No guardo / restaura contexts ni nada, lo que probablemente querrás hacer. Hay cosas que aún podría querer hacer para mejorar, como quizás insert la ruta 'sombra' si quiere usar un borde normal. Es posible que desee utilizar más / diferentes colors y ubicaciones. Pero esto debería comenzar.

ACTUALIZAR

Hay otro método que puede usar para crear este efecto. Escribí un algorithm para bisel las routes arbitrarias de bezier en charts centrales. Esto se puede usar para crear el efecto que está buscando. Este es un ejemplo de cómo lo uso en mi aplicación:

Biselado atrás botón

Pasas a la rutina el CGContextRef, CGPathRef, el tamaño del bisel y los colors que deseas usar para resaltar / ocultar.

El código que utilicé para esto se puede encontrar aquí: Github – Beveling Algorithm .

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

Usar la sombra de la capa no lo hará. Necesitas tanto una sombra exterior ligera como una sombra interior oscura para get ese efecto. Una capa solo puede tener una sombra (exterior). (Además, las sombras de capas se vuelven a dibujar dinámicamente y fuerzan la renderización basada en CPU que mata el performance).

Deberás hacer tu propio dibujo con CoreGraphics, ya sea en el método drawRect: método de drawRect: una vista o de drawInContext: una capa. (O dibuja en un context de image y luego reutiliza la image). Dicho dibujo usará principalmente las CGContext functions . (Llamaré algunos a continuación, pero este enlace tiene documentation para todos ellos).

Para un button networkingondo rect, puede resultarle tedioso crear el CGPath apropiado; en su lugar, puede usar +[UIBezierPath bezierPathWithRoundedRect:cornerRadius:] y luego la propiedad CGPath la ruta para establecer la ruta actual del context con CGContextAddPath .

Puede crear una sombra interna configurando una ruta de recorte (consulte CGContextClip y funciones relacionadas) con la forma del button, configurando una sombra (consulte CGContextSetShadowWithColor y funciones relacionadas) y, a continuación, dibujando alnetworkingedor del exterior de la forma que desea que aparezca en sombreado. Para la sombra interna, trazo ( CGContextStrokePath ) un round-rect que es un poco más grande que tu button, usando un ancho de trazo grueso ( CGContextSetLineWidth ) por lo que hay mucha "tinta" para generar una sombra (restring, este trazo no será visible debido a la ruta de recorte).

Puede crear una sombra externa de la misma manera: no use una ruta de recorte esta vez, porque desea que la sombra esté fuera de la forma y rellene ( CGContextFillPath ) la forma de su button en lugar de acariciarlo. Tenga en count que dibujar una sombra es una especie de "modo": guarde el estado de los charts ( CGContextSaveGState ), configure una sombra y luego dibuje la forma en que desea ver una sombra (la forma en sí no se dibuja cuando está en este modo), y finalmente restaurar el estado ( CGContextRestoreGState ) para salir del "modo de sombra". Como ese modo no dibuja la forma, solo la sombra, necesitarás dibujar la forma por separado.

También hay una order para hacer todo esto. Debería ser obvio si piensas en el order en que pintarías estas cosas con medios físicos: primero dibuja la sombra exterior, luego el button se llena y luego la sombra interior. Puede agregar un golpe después de eso si la sombra interna no le da un contorno suficientemente pronunciado.


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