2011-09-20 3 views
13

Je souhaite ajouter une ombre portée en utilisant CALayer à une vue UI (Image). Le code suivant fonctionne généralement bienUIView shadow et InterfaceBuilder

previewImage.layer.shadowColor = [[UIColor blackColor] CGColor]; 
previewImage.layer.shadowOffset = CGSizeMake(1.0f, 1.0f); 
previewImage.layer.shadowOpacity = 1.0f; 
previewImage.layer.shadowRadius = 8.0f; 

Cependant, cela ne fonctionne que si je crée ce point de vue et programme ajouter comme sous-vue à mon avis principale. Lorsque cette vue est configurée dans InterfaceBuilder et définie comme IBOutlet UIImageView, cela ne fonctionne PAS. Aucune ombre n'apparaît. Alors qu'est-ce qui me manque ici?

+1

tout à fait au hasard, je viens de découvrir le problème réel ne semble pas être InterfaceBuilder, mais le fait que je mets les clipsToBounds de ma vue InterfaceBuilder = OUI. N'ayant pas ici est correct. Donc je suppose que je vais devoir envelopper la vue dans une seconde vue avec clipsToBound = NO et l'ombre. Y a-t-il un autre moyen? – Dennis

+0

J'ai eu le même problème, mais c'était parce que "Clip Subviews" a été vérifié pour la vue dans Interface Builder. Décochez cette case pour rendre l'ombre CALayer visible. – azdev

Répondre

2

Je ne sais pas quel est le problème - assurez-vous que votre propriété UIImageViewclipsToBounds est définie sur NO. Vous pouvez le faire en viewDidLoad après le chargement du fichier nib en référençant votre IBOutlet. Vous ne devriez pas avoir besoin de l'envelopper dans une autre vue.

Modifier

À la lumière de vous avoir besoin de votre image à l'échelle à l'aide de remplissage d'aspect, vous pouvez utiliser la propriété contentsRect de la UIImageView « couche sous-jacente de la« Simuler l'effet du contenu de découpage. contentsRect est un rectangle dans l'espace de coordonnées de l'unité du contenu de la couche (dans ce cas, votre image) qui définit un sous-rectangle du contenu qui doit être dessiné.

Avec un peu de mathématiques, nous pouvons trouver ce rectangle en comparant la taille de l'affichage de l'image avec la taille de l'image (en tenant compte de l'aspect écaillage du remplissage):

CGSize imageViewSize = previewImage.size; 
CGSize imageSize = previewImage.image.size; 

// Find the scaling required for the image to fit the image view (as for aspect fill). 
CGFloat imageWidthScale = fabsf(imageViewSize.width/imageSize.width); 
CGFloat imageHeightScale = fabsf(imageViewSize.height/imageSize.height); 
CGFloat imageScale = (imageWidthScale > imageHeightScale) ? imageWidthScale : imageHeightScale; 

// Determine the new image size, after scaling. 
CGSize scaledImageSize = CGSizeApplyAffineTransform(imageSize, CGAffineTransformMakeScale(imageScale, imageScale)); 

// Set the layer's contentsRect property in order to 'clip' the image to the image view's bounds. 
previewImage.layer.contentsRect = CGRectMake(((scaledImageSize.width - imageViewSize.width)/2.0f)/scaledImageSize.width, 
              ((scaledImageSize.height - imageViewSize.height)/2.0f)/scaledImageSize.height, 
              imageViewSize.width/scaledImageSize.width, 
              imageViewSize.height/scaledImageSize.height); 

Faire cela, vous pouvez laisser clipsToBounds ensemble à NO pour votre vue d'image, mais l'image apparaîtra toujours coupée. Si vous avez besoin de changer la taille de la vue de l'image, il peut être pratique d'intégrer ce code dans une méthode qui prend un UIImageView comme paramètre.

J'espère que cette aide.

+0

Je suis d'accord. Aucun emballage nécessaire. Juste ne pas clipToBounds - comme l'ombre est en dehors des limites. – bandejapaisa

+0

La raison pour laquelle j'ai besoin de clipToBounds, c'est que cette image est trop grande pour s'adapter à la vue et j'ai besoin d'utiliser "Aspect Fill" pour la rendre belle. Je pourrais, bien sûr, recadrer l'image elle-même en la dessinant entièrement nouvelle, puis en obtenant un ajustement par le biais de UIGraphicsGetImageFromCurrentImageContext(). Mais ne serait-ce pas beaucoup de frais généraux? – Dennis

+0

@Dennis: Si l'image est statique et ne sera jamais utilisée à cette taille, alors ma première suggestion serait de recréer l'image dans votre logiciel de retouche d'image, puis de l'utiliser dans l'image de votre projet sans avoir à modifier sa géométrie. Si ce n'est pas une option, cependant, voir ma modification ci-dessus. – Stuart

14

Je sais que cette question a longtemps, mais récemment, j'étais dans une situation similaire, j'ai donc décidé de mettre ma réponse pour ceux qui sont dans une situation comme celle-ci.

Je voulais être en mesure de régler la borderColor et shadowColor sur une UIView par l'Interface Builder, mais le type de borderColor propriété d'une couche est CGColor (comme shadowColor) qui ne fait pas partie des types autorisés à modifier en la caractéristique des attributs d'exécution définie par l'utilisateur.

donc je l'ai fait une extension pour CALayer et j'ai ajouté deux propriétés appelées borderColorIB et shadowColorIB qui sont de type UIColor:

RuntimeAttributes.h

@import QuartzCore; 

@interface CALayer (IBConfiguration) 

@property(nonatomic, assign) UIColor* borderColorIB; 
@property(nonatomic, assign) UIColor* shadowColorIB; 

@end 

RuntimeAttributes.m

#import <UIKit/UIKit.h> 
#import "RuntimeAttributes.h" 

@implementation CALayer (IBConfiguration) 

-(void)setBorderColorIB:(UIColor*)color 
{ 
    self.borderColor = color.CGColor; 
} 

-(UIColor*)borderColorIB 
{ 
    return [UIColor colorWithCGColor:self.borderColor]; 
} 

-(void)setShadowColorIB:(UIColor*)color 
{ 
    self.shadowColor = color.CGColor; 
} 

-(UIColor*)shadowColorIB 
{ 
    return [UIColor colorWithCGColor:self.shadowColor]; 
} 

@end 

Maintenant, je serai alredy en mesure de définir ces deux propriétés par Interface Builder comme ceci:

  1. Dans la section 'Attributs d'exécution définis par l'utilisateur' (inspecteur Identity)
  2. Assurez-vous le UIView est sélectionné et ajouter les attributs d'exécution suivants:

    • layer.borderWidth, Nombre, 1
    • layer.borderColorIB, couleur, someColor <- my custom property to set the borderColor
    • layer.shadowColorIB, couleur, someColor <- my custom property to set the shadowColor
    • layer.shadowOpacity, Nombre, 0,8
    • layer.shadowOffset, la taille, {5,5}
    • couche
    • . cornerRadius, Nombre, 5

Voici une image pour vous montrer comment je l'ai fait:

enter image description here

... et le résultat sera évident lors de l'exécution, pas dans Xcode:

enter image description here

J'espère que cela peut aider certaines personnes là-bas!

16

Ajouter un fichier nommé UIView.swift dans votre projet (ou tout simplement coller ce dans un fichier):

import UIKit 

@IBDesignable extension UIView { 

    /* The color of the shadow. Defaults to opaque black. Colors created 
    * from patterns are currently NOT supported. Animatable. */ 
    @IBInspectable var shadowColor: UIColor? { 
     set { 
      layer.shadowColor = newValue!.CGColor 
     } 
     get { 
      if let color = layer.shadowColor { 
       return UIColor(CGColor:color) 
      } 
      else { 
       return nil 
      } 
     } 
    } 

    /* The opacity of the shadow. Defaults to 0. Specifying a value outside the 
    * [0,1] range will give undefined results. Animatable. */ 
    @IBInspectable var shadowOpacity: Float { 
     set { 
      layer.shadowOpacity = newValue 
     } 
     get { 
      return layer.shadowOpacity 
     } 
    } 

    /* The shadow offset. Defaults to (0, -3). Animatable. */ 
    @IBInspectable var shadowOffset: CGPoint { 
     set { 
      layer.shadowOffset = CGSize(width: newValue.x, height: newValue.y) 
     } 
     get { 
      return CGPoint(x: layer.shadowOffset.width, y:layer.shadowOffset.height) 
     } 
    } 

    /* The blur radius used to create the shadow. Defaults to 3. Animatable. */ 
    @IBInspectable var shadowRadius: CGFloat { 
     set { 
      layer.shadowRadius = newValue 
     } 
     get { 
      return layer.shadowRadius 
     } 
    } 
} 

Ensuite, ce sera disponible dans Interface Builder pour chaque vue dans le panneau Utilitaires> Attributs Inspecteur:

Utilities Panel

Vous pouvez facilement régler l'ombre maintenant.

Remarques:
- L'ombre n'apparaîtra qu'au moment de l'exécution.
- clipsToBounds doit être faux (par défaut, il est)