Ios 将UIView(覆盖)与应用程序背景混合
我想使用一种特殊的混合模式(在我的例子中是叠加模式)将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
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等价物(垂直滚动视图)本身包含在水平滚动视图中,以生成“页面”。是否真的没有办法告诉视图与它后面的任何视图混合,而不管视图层次如何?