3

I want to blend multiple UIView in such a way that it can move, rotate and resize smoothly.

I already achieved this with the code below but it's not as smooth as other application like PicsArt & SnapSeed.

- (void)drawRect:(CGRect)rect {
   // Drawing code

   [super drawRect:rect];

   UIImage *viewImage = [self captureView:self.superview withFrame:self.frame];

   [viewImage drawInRect:rect];

   [self.imageToBlend drawInRect:rect blendMode:self.blendMode alpha:self.alphaBlend];

  // Other code....  
}
Ketan Odedra
  • 1,215
  • 10
  • 35
  • I think I'm not getting your question, why aren't you using a CGAffineTransform? If you apply rotation, translation, on the hosting view it will automatically blend subviews in it and Core Animation will hardware accelerate everything – Andrea Jan 22 '19 at 14:29

2 Answers2

0

You can't blend views this way; they have separate layer hierarchies that are resolved independently. Move the gradient and text on CALayer objects and blend those (or draw them by hand inside the same view). For example:

class MyView: UIView {
    let gradientLayer: CAGradientLayer = {
        let gradientLayer = CAGradientLayer()
        gradientLayer.colors = [UIColor.red.cgColor,
                                UIColor.blue.cgColor]
        gradientLayer.startPoint = CGPoint(x: 0, y: 0)
        gradientLayer.endPoint = CGPoint(x: 1, y: 1)
        return gradientLayer
    }()

    let textLayer: CATextLayer = {
        let textLayer = CATextLayer()
        let astring = NSAttributedString(string: "Text Example",
                                         attributes: [.font: UIFont(name: "MarkerFelt-Wide", size: 60)!])
        textLayer.string = astring
        textLayer.alignmentMode = kCAAlignmentCenter
        textLayer.bounds = astring.boundingRect(with: CGSize(width: .max, height: .max),
                                                options: [.usesLineFragmentOrigin, .usesFontLeading], context: nil)
        return textLayer
    }()

    override func draw(_ rect: CGRect) {
        if let context = UIGraphicsGetCurrentContext() {
            gradientLayer.bounds = bounds
            gradientLayer.render(in: context)

            context.saveGState()
            context.setBlendMode(.softLight)

            context.translateBy(x: center.x - textLayer.bounds.width / 2,
                                y: center.y - textLayer.bounds.height / 2)

            textLayer.position = center
            textLayer.render(in: context)
            context.restoreGState()
        }
    }
}

you won't get a proper answer by searching try your own logic.

By using context, you can use a gesture and all other.

viki
  • 41
  • 1
  • 5
0

Capture and blend each time when drawRect: sounds too heavy.

It seems that you want to blend some image to its backend with various blend modes and alpha.

In that case, you'd better to blend entire view, and use view.layer.mask to show only masked area of the blend result.

So you can just move, rotate and resize the view.layer.mask, and no capturing or blending is needed. It should be much faster.

Something like this.

class MyView: UIImageView {
    var backendImage: UIImage?
    var blendImage: UIImage?
    var blendMode: CGBlendMode = .normal
    var alphaBlend: Float = 0.5

    func setup(backend: UIView) {
        self.backendImage = self.captureView(backend)
        self.image = self.createBlendImage()
        self.layer.mask = self.createMask()
    }

    func captureView(view: UIView) -> UIImage {
        // capture view
    }

    func createBlendImage() -> UIImage {
        // prepare ImageContext, draw two image, get image, and so on...
    }

    func createMask() -> CALayer {
        let mask = CAShapeLayer()
        // setup the shape of mask
    }

    func moveMask(origin: CGPoint, scale: Float, rotation: Float) {
        // change mask
        self.layer.mask.frame = ...
        self.layer.mask.transform = ...
    }
}
taka
  • 1,407
  • 5
  • 7