我创建此图像是为了说明我的问题。
我有一个按钮,按住时会将背景更改为粉红色。 然后在两个按钮之间显示一个视图来显示视图。 然后顶部的另一个按钮添加内部阴影。
我无法让两个目标同时工作。
这可以实现吗?
我尝试使用 HitTest 但没有成功,因为它将点击传递给下面的按钮,但随后第一个按钮失去了目标。
我正在考虑使用 UIViews 来实现与我在按钮目标内使用图层来实现我想要的效果相同的效果。
我只需要两个可以同时接收触摸的视图。我更喜欢 UIButton 解决方案,因为按住触摸几乎是瞬时的,而长按手势需要一些时间才能生效。
我已经解决这个问题很长一段时间了,所以我决定寻求一些帮助。
您(可能)可以通过使用
UIView
并处理触摸而不是使用 UIButton
... 更轻松地完成此操作
首先,让我们创建一个基本的“触摸时更改背景”视图:
class HighlightView: UIView {
// very light gray
var normalColor: UIColor = UIColor(red: 0.95, green: 0.95, blue: 0.95, alpha: 1.0)
// light pink
var hightlightColor: UIColor = UIColor(red: 1.0, green: 0.9, blue: 1.0, alpha: 1.0)
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
private func commonInit() {
backgroundColor = normalColor
}
func doHighlight(_ b: Bool) {
backgroundColor = b ? hightlightColor : normalColor
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
backgroundColor = hightlightColor
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let t = touches.first else { return }
let pt = t.location(in: self)
// if touch is inside my bounds
// use highlight color
// else, touch moved outside so
// use normal color
if bounds.contains(pt) {
backgroundColor = hightlightColor
} else {
backgroundColor = normalColor
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
backgroundColor = normalColor
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
backgroundColor = normalColor
}
}
带有示例视图控制器:
class TouchHoldVC: UIViewController {
let holdView = HighlightView()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemBackground
holdView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(holdView)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
holdView.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
holdView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
holdView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
holdView.heightAnchor.constraint(equalToConstant: 200.0),
])
}
}
在
init
我们将背景颜色设置为“正常”...
touchesBegan
- 我们将背景颜色设置为“突出显示”看起来像这样:
接下来我们可以创建一个“内阴影视图”类:
class InnerShadowView: UIView {
// "inner" shadow
private let innerShadowLayer = CAShapeLayer()
private let innerShadowMaskLayer = CAShapeLayer()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
private func commonInit() -> Void {
// add sublayer
self.layer.addSublayer(innerShadowLayer)
// fillColor doesn't matter - just needs to be opaque
innerShadowLayer.fillColor = UIColor.white.cgColor
innerShadowLayer.fillRule = .evenOdd
}
override func layoutSubviews() {
super.layoutSubviews()
// for the "inner" shadow,
// rectangle path needs to be larger than
// bounds + shadow offset + shadow raidus
// so the shadow doesn't "bleed" from all sides
let path = UIBezierPath(rect: bounds.insetBy(dx: -40, dy: -40))
// create a path for the "hole" in the layer
let holePath = UIBezierPath(rect: bounds)
// this "cuts a hole" in the path
path.append(holePath)
path.usesEvenOddFillRule = true
innerShadowLayer.path = path.cgPath
// mask the layer, so we only "see through the hole"
innerShadowMaskLayer.path = holePath.cgPath
innerShadowLayer.mask = innerShadowMaskLayer
// adjust properties as desired
innerShadowLayer.shadowOffset = .zero
innerShadowLayer.shadowColor = UIColor.black.cgColor
innerShadowLayer.shadowRadius = 5
// setting .shadowOpacity to a very small value (such as 0.025)
// results in very light shadow
// set .shadowOpacity to 1.0 to clearly see
// what the shadow is doing
innerShadowLayer.shadowOpacity = 0.75
}
}
通过添加
HighlightView
作为子视图来修改 InnerShadowView
:
class HighlightView: UIView {
var normalColor: UIColor = UIColor(red: 0.95, green: 0.95, blue: 0.95, alpha: 1.0)
var hightlightColor: UIColor = UIColor(red: 1.0, green: 0.9, blue: 1.0, alpha: 1.0)
let shadView = InnerShadowView()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
private func commonInit() {
shadView.translatesAutoresizingMaskIntoConstraints = false
addSubview(shadView)
let g = self
NSLayoutConstraint.activate([
shadView.topAnchor.constraint(equalTo: g.topAnchor, constant: 0.0),
shadView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 0.0),
shadView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0.0),
shadView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: 0.0),
])
backgroundColor = normalColor
shadView.isHidden = true
}
func doHighlight(_ b: Bool) {
backgroundColor = b ? hightlightColor : normalColor
shadView.isHidden = !b
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
doHighlight(true)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let t = touches.first else { return }
let pt = t.location(in: self)
if bounds.contains(pt) {
doHighlight(true)
} else {
doHighlight(false)
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
doHighlight(false)
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
doHighlight(false)
}
}
现在使用相同的示例控制器:
touchesBegan
- 我们将背景颜色设置为“突出显示”并且我们显示阴影视图看起来像这样:
接下来,一个带有图像和标签的简单“内容视图”:
class SomeContentView: UIView {
let label = UILabel()
let imgView = UIImageView()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
private func commonInit() {
label.translatesAutoresizingMaskIntoConstraints = false
addSubview(label)
imgView.translatesAutoresizingMaskIntoConstraints = false
addSubview(imgView)
let pad: CGFloat = 8.0
let g = self
NSLayoutConstraint.activate([
label.topAnchor.constraint(equalTo: g.topAnchor, constant: pad),
label.leadingAnchor.constraint(equalTo: imgView.trailingAnchor, constant: 12.0),
label.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -16.0),
label.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -pad),
imgView.topAnchor.constraint(equalTo: g.topAnchor, constant: pad),
imgView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: pad),
//imgView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0.0),
imgView.widthAnchor.constraint(equalToConstant: 80.0),
imgView.heightAnchor.constraint(equalTo: imgView.widthAnchor, multiplier: 1.0),
imgView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -pad),
])
label.numberOfLines = 0
label.textAlignment = .center
label.text = "This an image view and a label, in a view we will call \"SomeContentView\""
if let img = UIImage(named: "test300x300") {
imgView.image = img
}
}
}
它本身看起来像这样:
我们将向我们的
HighlightView
-- func addContent(_ cView: UIView)
-- 添加一个函数,这样我们就可以将其添加为子视图:
class HighlightView: UIView {
var normalColor: UIColor = UIColor(red: 0.95, green: 0.95, blue: 0.95, alpha: 1.0)
var hightlightColor: UIColor = UIColor(red: 1.0, green: 0.9, blue: 1.0, alpha: 1.0)
let shadView = InnerShadowView()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
private func commonInit() {
shadView.translatesAutoresizingMaskIntoConstraints = false
addSubview(shadView)
let g = self
NSLayoutConstraint.activate([
shadView.topAnchor.constraint(equalTo: g.topAnchor, constant: 0.0),
shadView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 0.0),
shadView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0.0),
shadView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: 0.0),
])
backgroundColor = normalColor
shadView.isHidden = true
}
func doHighlight(_ b: Bool) {
backgroundColor = b ? hightlightColor : normalColor
shadView.isHidden = !b
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
doHighlight(true)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let t = touches.first else { return }
let pt = t.location(in: self)
if bounds.contains(pt) {
doHighlight(true)
} else {
doHighlight(false)
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
doHighlight(false)
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
doHighlight(false)
}
func addContent(_ cView: UIView) {
cView.translatesAutoresizingMaskIntoConstraints = false
insertSubview(cView, at: 0)
let g = self
NSLayoutConstraint.activate([
cView.topAnchor.constraint(equalTo: g.topAnchor, constant: 0.0),
cView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 0.0),
cView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0.0),
cView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: 0.0),
])
}
}
并对示例控制器进行一些更改 - 我们将添加
SomeContentView
的实例并让它的约束设置高度:
class TouchHoldVC: UIViewController {
let holdView = HighlightView()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemBackground
holdView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(holdView)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
holdView.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
holdView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
holdView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
// the content view will set the height of the "touch hold" Highlight view
//holdView.heightAnchor.constraint(equalToConstant: 200.0),
])
holdView.addContent(SomeContentView())
}
}
我们得到这个: