UIView shadow и InterfaceBuilder

Я хочу добавить тень с использованием CALayer в представление пользовательского интерфейса (изображения). Следующий код работает в целом отлично

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

Однако это работает только в том случае, если я создаю этот вид программно и добавлю его в качестве подзаголовка к моему основному виду. Когда это представление настроено в InterfaceBuilder и определено как IBOutlet UIImageView, это НЕ работает. Тень не появляется. Так что мне здесь не хватает?

Ответ 1

Я не уверен, в чем проблема: убедитесь, что для вашего свойства UIImageView clipsToBounds установлено значение NO. Вы можете сделать это в viewDidLoad после загрузки из файла nib путем ссылки на ваш IBOutlet. Вам не нужно обертывать его в другое представление.

Edit

В свете того, что вам нужно масштабировать изображение, используя аспект заполнения, вы можете использовать свойство contentsRect лежащего слоя UIImageView для "имитации" эффекта отсечения содержимого. contentsRect - прямоугольник в единичном координатном пространстве содержимого слоя (в данном случае ваш образ), который определяет суб-прямоугольник содержимого, которое должно быть нарисовано.

С небольшим количеством математики мы можем найти этот прямоугольник, сравнив размер изображения с размером изображения (учитывая масштаб заполнения аспект):

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 contentsRect property in order to 'clip' the image to the image view 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);

Для этого вы можете оставить clipsToBounds на NO для вашего изображения, но изображение по-прежнему будет обрезано. Если вам вообще необходимо изменить размер изображения, может оказаться удобным включить этот код в метод, который принимает параметр UIImageView в качестве параметра.

Надеюсь, это поможет.

Ответ 2

Добавьте в проект файл UIView.swift(или просто вставьте его в любой файл):

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
        }
    }
}

Затем это будет доступно в Interface Builder для каждого вида в панели "Утилиты" > "Attributes Inspector":

Панель Утилиты

Теперь вы можете легко установить тень.

Примечания:
 - Тень появится только во время выполнения.
 - clipsToBounds должно быть ложным (по умолчанию оно)

Ответ 3

Я знаю, что этот вопрос длинный, но в последнее время я был в подобной ситуации, поэтому я решил поставить свой ответ для тех, кто находится в такой ситуации.

Я хотел иметь возможность устанавливать borderColor и shadowColor на UIView через построитель интерфейсов, но тип свойства borderColor borderColor - тип CGColor), который не является одним из типов, разрешенных для изменения в пользовательской функции атрибутов времени выполнения.

Итак, я сделал расширение для CALayer, и я добавил два свойства, называемые borderColorIB и shadowColorIB, которые имеют тип 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

Теперь я могу установить эти два свойства через Interface Builder следующим образом:

  • В разделе "Определения пользовательского времени выполнения" (Инспектор удостоверений)
  • Убедитесь, что выбран UIView, и добавьте следующие атрибуты выполнения:

    • layer.borderWidth, Number, 1
    • layer.borderColorIB, цвет, someColor <- my custom property to set the borderColor
    • layer.shadowColorIB, Цвет, someColor <- my custom property to set the shadowColor
    • layer.shadowOpacity, Number, 0.8
    • layer.shadowOffset, size, {5,5}
    • layer.cornerRadius, Number, 5

Вот изображение, чтобы показать вам, как я это сделал:

enter image description here

... и результат будет проявляться во время выполнения, а не в Xcode:

enter image description here

Надеюсь, это поможет некоторым людям!