Ios 将UIView(覆盖)与应用程序背景混合

Ios 将UIView(覆盖)与应用程序背景混合,ios,swift,calayer,core-image,alphablending,Ios,Swift,Calayer,Core Image,Alphablending,我想使用一种特殊的混合模式(在我的例子中是叠加模式)将UIView与我的应用程序的背景混合。但是,要混合的视图包含在复杂的视图层次中 可以使用view.layer.compositingFilter=“overlayBlendMode”实现视图与其直接同级视图的混合,但视图不会与非同级视图混合,如应用程序背景 为了重现这个问题,我做了以下步骤: import UIKit import PlaygroundSupport class MyViewController : UIViewContro

我想使用一种特殊的混合模式(在我的例子中是叠加模式)将UIView与我的应用程序的背景混合。但是,要混合的视图包含在复杂的视图层次中

可以使用
view.layer.compositingFilter=“overlayBlendMode”
实现视图与其直接同级视图的混合,但视图不会与非同级视图混合,如应用程序背景

为了重现这个问题,我做了以下步骤:

import UIKit
import PlaygroundSupport

class MyViewController : UIViewController {
    override func loadView() {
        let parentView = UIView()
        parentView.backgroundColor = .purple

        // Child view
        let childView = UIView(frame: CGRect(x: 50, y: 50, width: 200, height: 200))
        childView.layer.borderColor = UIColor.orange.cgColor
        childView.layer.borderWidth = 3
        parentView.addSubview(childView)

        // Child child view
        let childChildView = UIView(frame: CGRect(x: 50, y: 50, width: 100, height: 50))
        childChildView.backgroundColor = .white
        childChildView.layer.compositingFilter = "overlayBlendMode"
        childView.addSubview(childChildView)

        self.view = parentView
    }
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()
我们可以在这里看到,白色的子视图没有混合:

而视图应如下所示混合显示(边框不应更改颜色):

为了创建第二张图片,我在
childView
上应用了合成过滤器,而不是
childView
,它将混合所有其他子视图-因此这不是我想要的。我只想混合这个特定的视图

注意:此视图应该移动,因为它位于UIScrollView中

编辑:具有图像背景和滚动视图的更复杂示例

import UIKit
import PlaygroundSupport

class MyViewController : UIViewController {

    override func loadView() {
        let parentView = UIView()

        // Background image
        let backgroundImageView = UIImageView(image: UIImage(named: "image.jpg")!)
        backgroundImageView.frame = UIScreen.main.bounds
        parentView.addSubview(backgroundImageView)

        // Page view (horizontal scrollview)
        let pageView = UIScrollView(frame: CGRect(x: 50, y: 50, width: 200, height: 200))
        pageView.contentSize = CGSize(width: 600, height: 200)
        pageView.flashScrollIndicators()
        pageView.layer.borderColor = UIColor.orange.cgColor
        pageView.layer.borderWidth = 3
        parentView.addSubview(pageView)

        // Child view (vertical scrollview)
        let childView = UIScrollView(frame: CGRect(x: 20, y: 20, width: 100, height: 150))
        childView.contentSize = CGSize(width: 100, height: 300)
        childView.layer.borderColor = UIColor.green.cgColor
        childView.layer.borderWidth = 3
        pageView.addSubview(childView)

        // Child child view
        let childChildView = UIView(frame: CGRect(x: 50, y: 50, width: 100, height: 50))
        childChildView.backgroundColor = .white
        childChildView.layer.compositingFilter = "overlayBlendMode"
        childView.addSubview(childChildView)

        self.view = parentView
    }

}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()

与子视图背景是否清晰相关的问题,因此“混合”为白色。能否将子视图背景颜色设置为与应用程序背景相同,然后仅在childView中混合childView?

更新2:

我尝试了几种方法,包括添加图层或创建自定义图像过滤器,使用背景图像作为输入图像,但这些解决方案都没有达到预期效果。主要问题始终是视图层次结构

我可能已经找到了一个解决方案,在创建childView之后但在显示之前,使用视图的生成图像或实际背景图像作为childView的内容背景。我对示例代码做了一些修改,在parentView中添加了滚动视图和背景图像。看看这是否适合你/是你想要的结果:

    import UIKit
import PlaygroundSupport

class MyViewController : UIViewController {

    override func loadView() {
        let parentView = UIView()
        parentView.backgroundColor = .purple

        let imageName = "image.jpg"
        let image = UIImage(named: imageName)
        let imageWidth = Int((image?.size.width)!)
        let imageheight = Int((image?.size.height)!)
        let imageView = UIImageView(image: image!)
        imageView.frame = CGRect(x: 50, y: 50, width: imageWidth , height: imageheight)
        parentView.addSubview(imageView)

        // Child view as UIScrollView
        let childView = UIScrollView(frame: CGRect(x: 55, y: 55, width: imageWidth - 10, height: imageheight - 10 ))
        childView.contentSize = CGSize(width: imageWidth - 10, height: 5000)
        childView.layer.borderColor = UIColor.orange.cgColor
        childView.flashScrollIndicators()
        childView.layer.borderWidth = 10
        parentView.addSubview(childView)

        // ChildChild view
        let childChildView = UIView(frame: CGRect(x: 15, y: 100, width: 85, height: imageheight - 180))
        childChildView.layer.compositingFilter = "overlayBlendMode"
        childChildView.backgroundColor = .white

        //Creating a static image of the background views BEFORE adding the childChildView.
        let format = UIGraphicsImageRendererFormat()
        format.scale = 1
        format.preferredRange = .standard ///color profile
        ///Change the imageView to the parentView size of the app. Not available if not set in the playground.
        let renderer = UIGraphicsImageRenderer(size: imageView.bounds.size, format: format)
        let imageBG = renderer.image { context in
            ///This draws all subviews of the parentView one after the other.
            ///Because the background image is not a parent of our current view, otherwise childView.drawHierachy would have been enough
            for subview in parentView.subviews {
                    ///Skip specific views or view classes you don't want to be added to the image. or if you only need the parentView itself rendered remove the for in loop.
                    subview.drawHierarchy(in: imageView.bounds, afterScreenUpdates: true)
            }
        }
        //Adding the static background image. This could simply also be the actual image: UIImage if no other views are supposed to be used.
        childView.layer.contents = imageBG.cgImage

        childView.addSubview(childChildView)

        self.view = parentView
    }

}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()
其结果如下:

更新:

图像中的颜色具有误导性,因为您可以假设正常的透明度效果是相同的。但正如Coconuts所指出的,重叠模式是完全不同的。我假设问题是compositingFilter仅适用于下面的视图,即使该视图是透明的。 我试图找到一个解决办法,使用一个遮罩,从childview中切出一个与childchild大小相同的正方形。但这也不起作用,因为遮罩也应用于所有子视图。我让它工作的唯一方法是将childview改为childview的兄弟视图,或者是背景视图的直接子视图。但不确定这在Coconuts提到的复杂视图层次结构中是否可行

    // Sibling view with adjusted x and y
    let childChildView = UIView(frame: CGRect(x: 100, y: 100, width: 100, height: 50))
    childChildView.backgroundColor = .white
    childChildView.layer.compositingFilter = "overlayBlendMode"

    parentView.addSubview(childChildView)
杂项:

仅获取示例图像的视觉结果,而不是按照Coco的要求实际使用OverlyBlendMode过滤器

如果只需要混合颜色,可以更改颜色的alpha值

    // Child child view
    let childChildView = UIView(frame: CGRect(x: 50, y: 50, width: 100, height: 50))
    childChildView.backgroundColor = UIColor(white: 1, alpha: 0.5)
    //childChildView.layer.compositingFilter = "overlayBlendMode"
    childView.addSubview(childChildView)
或者试试这个:

    // Child child view
    let childChildView = UIView(frame: CGRect(x: 50, y: 50, width: 100, height: 50))
    childChildView.backgroundColor = .white
    childChildView.layer.opacity = 0.5
    childView.addSubview(childChildView)
具有多个滚动视图时的其他尝试:

这是一种尝试,旨在解决添加了具有多个滚动视图的更复杂视图层次的问题。当应用程序更新(重画)其视图时,需要提高性能,或者调整背景图像层背景图像的部分需要同步运行。目前它落后了一点

    import UIKit
import PlaygroundSupport

class MyViewController : UIViewController {

    override func loadView() {

        let parentView = UIView()

        // Background image
        let backgroundImageView = UIImageView(image: UIImage(named: "image.jpg")!)
        backgroundImageView.frame = UIScreen.main.bounds
        parentView.addSubview(backgroundImageView)

        // Page view (horizontal scrollview)
        let pageView = UIScrollView(frame: CGRect(x: 50, y: 50, width: 200, height: 200))
        pageView.contentSize = CGSize(width: 600, height: 200)
        pageView.flashScrollIndicators()
        pageView.layer.borderColor = UIColor.yellow.cgColor
        pageView.layer.borderWidth = 3
        pageView.clipsToBounds = true
        parentView.addSubview(pageView)

        // Child view (vertical scrollview)
        let childView = UIScrollView(frame: CGRect(x: 20, y: 20, width: 100, height: 150))
        childView.contentSize = CGSize(width: 100, height: 300)
        childView.layer.borderColor = UIColor.red.cgColor
        childView.layer.borderWidth = 3
        pageView.addSubview(childView)

        // Child child view
        let childChildView = UIView(frame: CGRect(x: 50, y: 50, width: 50, height: 50))

        //Child child view foreground sublayer
        let childChildFrontLayer = CALayer()
        childChildFrontLayer.frame = childChildView.frame.offsetBy(dx: -75, dy: -50)
        childChildFrontLayer.backgroundColor = UIColor.white.cgColor
        childChildFrontLayer.compositingFilter = "overlayBlendMode"

        //Child child view background sublayer
        let childChildBackLayer = CALayer()
        childChildBackLayer.contents = UIImage(named: "image.jpg")?.cgImage
        var absolutFrame = parentView.convert(childChildView.frame, from: childView)
        childChildBackLayer.frame = CGRect(x: -absolutFrame.minX, y: -absolutFrame.minY, width: backgroundImageView.frame.width, height: backgroundImageView.frame.height)

        childChildView.layer.addSublayer(childChildBackLayer)
        childChildView.layer.addSublayer(childChildFrontLayer)
        childView.addSubview(childChildView)

        //Checking for any scrolling. Is slightly faster then the scollview delegate methods but might cause main thread checker warning.
        DispatchQueue.global(qos: .userInteractive).async {
            while true {
                if pageView.isDragging || pageView.isTracking || pageView.isDecelerating || childView.isDragging || childView.isTracking || childView.isDecelerating {
                    absolutFrame = parentView.convert(childChildView.frame, from: childView)
                    DispatchQueue.main.async {
                        childChildBackLayer.frame = CGRect(x: -absolutFrame.minX, y: -absolutFrame.minY, width: backgroundImageView.frame.width, height: backgroundImageView.frame.height)
                    }
                }
            }
        }
        self.view = parentView
    }
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()

我的应用程序背景实际上可以是渐变或图像,所以我不认为我可以这样做:/I我认为iOS不支持
compositingFilter
(如文档中所述)。。。这在设备上真的有效吗?是的,它在设备上也有效,我不知道为什么在文档中写的是相反的。整个要点是能够使用一种特殊的混合模式(在我的例子中是叠加模式)进行混合。我是否正确理解,您想要使用叠加混合模式(如果您不仅使用紫色背景,我想这会产生不同的效果。)但它似乎不起作用,因为childview基本上是在尝试将过滤器应用于childview(即使此视图只有边框,但没有背景色?)是的,ChildView仅与它的直接同级视图混合,而我希望它与它后面的所有视图混合,直到应用程序背景。关于您的更新,我确实不可能在层次结构中向上移动视图,因为它包含在可滚动的scrollview中-另外,这将是整个应用程序视图层次结构y一团糟:/好吧,希望这对你有用。否则我就没主意了。;)这是向前迈出的一步!然而,这提出了两点:1)当childView小于图像视图时(当scrollview没有占据整个页面时),如何处理;2)当childView本身位于另一个scrollview中时,如何处理?在我的例子中,childView等价物(垂直滚动视图)本身包含在水平滚动视图中,以生成“页面”。是否真的没有办法告诉视图与它后面的任何视图混合,而不管视图层次如何?