Вырезать прозрачное отверстие в UIView - PullRequest
43 голосов
/ 15 марта 2012

Создание представления с прозрачной рамкой внутри него, чтобы виды за представлением можно было видеть через эту прозрачную рамку, но области вне этого не будут видны сквозь.Так что, по сути, это окно в представлении.

Надеемся, что сможем сделать что-то вроде этого:

 CGRect hole = CGRectMake(100, 100, 250, 250);
CGContextRef context = UIGraphicsGetCurrentContext();

CGContextSetFillColorWithColor(context, [UIColor blackColor].CGColor);
CGContextFillRect(context, rect);

CGContextAddRect(context, hole);
CGContextClip(context);

CGContextSetFillColorWithColor(context, [UIColor clearColor].CGColor);
CGContextFillRect(context, rect);

, но очистка не отменяет черный, поэтому весь фон черный.Любые идеи в этом направлении?

Ответы [ 17 ]

0 голосов
/ 21 апреля 2017

Включая ответ для Xamarin Studio iOS с использованием C #. Это рисует один прямоугольник с закругленными углами с 60% альфа. В основном взято из ответа от @mikeho

public override void Draw(CGRect rect)
{
    base.Draw(rect);

    //Allows us to draw a nice clear rounded rect cutout
    CGContext context = UIGraphics.GetCurrentContext();

    // Create a path around the entire view
    UIBezierPath clipPath = UIBezierPath.FromRect(rect);

    // Add the transparent window to a sample rectangle
    CGRect sampleRect = new CGRect(0f, 0f, rect.Width * 0.5f, rect.Height * 0.5f);
    UIBezierPath path = UIBezierPath.FromRoundedRect(sampleRect, sampleRect.Height * 0.25f);
    clipPath.AppendPath(path);

    // This sets the algorithm used to determine what gets filled and what doesn't
    clipPath.UsesEvenOddFillRule = true;

    context.SetFillColor(UIColor.Black.CGColor);
    context.SetAlpha(0.6f);

    clipPath.Fill();
}
0 голосов
/ 09 марта 2017

в этом коде создать больше, чем круг

- (void)drawRect:(CGRect)rect {

    // Drawing code
    UIColor *bgcolor=[UIColor colorWithRed:0.85 green:0.85 blue:0.85 alpha:1.0f];//Grey

    [bgcolor setFill];
    UIRectFill(rect);

    if(!self.initialLoad){//If the view has been loaded from next time we will try to clear area where required..

        // clear the background in the given rectangles
        for (NSValue *holeRectValue in _rectArray) {
            CGContextRef context = UIGraphicsGetCurrentContext();

            CGRect holeRect = [holeRectValue CGRectValue];

            [[UIColor clearColor] setFill];

            CGRect holeRectIntersection = CGRectIntersection( holeRect, rect );

            CGContextSetFillColorWithColor( context, [UIColor clearColor].CGColor );
            CGContextSetBlendMode(context, kCGBlendModeClear);

            CGContextFillEllipseInRect( context, holeRectIntersection );

        }
    }

    self.initialLoad=NO;
}
0 голосов
/ 15 марта 2012

Что ж, мне придется ответить, так как пропустил комментарий и заполнил форму ответа :) Мне бы очень хотелось, чтобы Карстен предоставил больше информации о том, как лучше всего делать то, что он предлагает.

Вы можете использовать

+ (UIColor *)colorWithPatternImage:(UIImage *)image

для создания фонового «цветного» изображения любой сложности.Изображение может быть создано либо программно, если вы знакомы с классами рисования, либо статически, если рамки окон заданы заранее.

0 голосов
/ 16 марта 2012

Закончено "Подделка"

windowFrame является свойством

CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [UIColor clearColor].CGColor);
CGContextFillRect(context, rect);
CGRect rootFrame = [[Navigation rootController] view].frame;

CGSize deviceSize = CGSizeMake(rootFrame.size.width, rootFrame.size.height);

CGRect topRect = CGRectMake(0, 0, deviceSize.width, windowFrame.origin.y);
CGRect leftRect = CGRectMake(0, topRect.size.height, windowFrame.origin.x, windowFrame.size.height);
CGRect rightRect = CGRectMake(windowFrame.size.width+windowFrame.origin.x, topRect.size.height, deviceSize.width-windowFrame.size.width+windowFrame.origin.x, windowFrame.size.height);
CGRect bottomRect = CGRectMake(0, windowFrame.origin.y+windowFrame.size.height, deviceSize.width, deviceSize.height-windowFrame.origin.y+windowFrame.size.height);

CGContextSetFillColorWithColor(context, [UIColor blackColor].CGColor);
CGContextFillRect(context, topRect);
CGContextFillRect(context, leftRect);
CGContextFillRect(context, rightRect);
CGContextFillRect(context, bottomRect);
0 голосов
/ 11 марта 2019

Этого можно добиться, задав слой вида границам.

class HollowSquareView: UIView {
  override func awakeFromNib() {
    super.awakeFromNib()

    self.backgroundColor = UIColor.clear

    self.layer.masksToBounds = true
    self.layer.borderColor = UIColor.black.cgColor
    self.layer.borderWidth = 10.0
  }
}

Это даст вам квадратную рамку шириной 10 и прозрачное ядро.

Вы также можете установить слой cornerRadius равным половине ширины вида, и это даст вам полый круг.

0 голосов
/ 10 апреля 2019

Я использовал ответ от Бушры Шахида, и он работал хорошо, но у него есть проблема, если круги накладываются друг на друга.

Я использовал этот другой подход, который хорошо работает в таких случаях:

class HoleView: UIView {
    var holes: [CGRect] = [] {
        didSet {
            lastProcessedSize = .zero
            createMask()
        }
    }

    private var lastProcessedSize = CGSize.zero

    override func layoutSubviews() {
        super.layoutSubviews()
        createMask()
    }

    private func createMask() {
        guard lastProcessedSize != frame.size,
            holes.count > 0
            else { return }

        let size = frame.size

        // create image
        UIGraphicsBeginImageContextWithOptions(size, false, UIScreen.main.scale)
        guard let context = UIGraphicsGetCurrentContext()
            else { return }

        UIColor.white.setFill()
        context.fill(CGRect(origin: .zero, size: size))

        UIColor.black.setFill()
        holes.forEach { context.fillEllipse(in: $0) }

        // apply filter to convert black to transparent
        guard let image = UIGraphicsGetImageFromCurrentImageContext(),
            let cgImage = image.cgImage,
            let filter = CIFilter(name: "CIMaskToAlpha")
            else { return }

        filter.setDefaults()
        filter.setValue(CIImage(cgImage: cgImage), forKey: kCIInputImageKey)
        guard let result = filter.outputImage,
            let cgMaskImage = CIContext().createCGImage(result, from: result.extent)
            else { return }

        // Create mask
        let mask = CALayer()
        mask.frame = bounds
        mask.contents = cgMaskImage
        layer.mask = mask
    }
}

В итоге:

  • Вы создаете маску UIImage в черно-белом режиме.вместо / прозрачного.
  • Используйте CIMaskToAlpha CIFilter, чтобы преобразовать его в прозрачную / белую маску.
  • Используйте сгенерированный CGImage в качестве содержимогоa CALayer
  • Пользователь tht CALayer в качестве маски просмотра.
0 голосов
/ 15 марта 2012

Сделай это наоборот! Поместите те виды, которые вы хотели бы видеть через «дыру» в отдельном виде нужного размера. Затем установите "clipsToBounds" в YES и поместите этот вид сверху. Вид с «прозрачной» рамкой - самый нижний. «clipsToBounds» означает, что все, что находится вне коробки / отверстия, обрезано.

Тогда вам, возможно, придется разобраться, как обрабатываются касания. Но это другой вопрос. Может быть, достаточно установить userInteractionEnabled на соответствующие представления.

...