当上面没有任何东西时,UISegmentedControl 并不棘手
UISegmentedControl not intractable when there is nothing above it
我的问题非常具体,我必须为此提供代码,抱歉,如果代码太复杂或太复杂,我对 UIKit 有点陌生,因为我更多地学习 SwiftUI 而不是 UIKit。
我有一个非常复杂的 UITableView
header,有一些不错的动画等等,我在上面放了一个 UISegmentedControl
。但是,一旦 UITableViewCell
落后于 UISegmentedControl
,它就会停止工作,并且 UISegmentedControl
.
会失去用户交互功能
如果没有视觉上下文和代码,这很难解释,所以就在这里。
如您所见,在顶部,用户可以自由地与 UISegmentedControl 交互,但是当我开始滚动并且 table 视图单元格在 UISegmentedControl
后面时(正如预期的那样),分段控件完全停止响应用户触摸输入。
再次,请原谅我的长(而且可能是坏的)代码,我还在学习。
这是我为 UITableView 编写的代码:
class TertiaryProfileScroll: UITableViewController {
var segmentedControl: UISegmentedControl!
var testBlurView: UIVisualEffectView!
var headerTitle: UIStackView!
var blurView: UIVisualEffectView!
var scoreRect: UIView!
var scoreLabel: UILabel!
var originalBlurRect: CGRect!
var originalTitleRect: CGRect!
var originalTestRect: CGRect!
override func viewDidLoad() {
super.viewDidLoad()
let headerView = SecondaryStretchyTableHeaderView(frame: CGRect(x: 0, y: 0, width: view.bounds.width, height: 488))
let image = UIImage(named: "background_image")
UIGraphicsBeginImageContextWithOptions(CGSize(width: 350, height: 350), false, 4)
image!.draw(in: CGRect(origin: CGPoint(x: 0, y: 0), size: CGSize(width: 350, height: 350)))
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
tableView.allowsSelection = false
tableView.separatorStyle = .none
headerView.imageView.image = newImage
tableView.tableHeaderView = headerView
addTitle()
blurView.contentView.isUserInteractionEnabled = true
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
}
override func scrollViewDidScroll(_ scrollView: UIScrollView) {
guard tableView.tableHeaderView != nil else { return }
let headerView = self.tableView.tableHeaderView as! SecondaryStretchyTableHeaderView
let headerGeometry = self.geometry(view: headerView, scrollView: scrollView)
let titleGeometry = self.geometry(view: headerTitle, scrollView: scrollView)
(tableView.tableHeaderView as! SecondaryStretchyTableHeaderView).containerView
.alpha = CGFloat(sqrt(headerGeometry.largeTitleWeight))
(tableView.tableHeaderView as! SecondaryStretchyTableHeaderView).imageContainer.alpha = CGFloat(sqrt(headerGeometry.largeTitleWeight))
let largeTitleOpacity = (max(titleGeometry.largeTitleWeight, 0.5) - 0.5) * 2
let tinyTitleOpacity = 1 - min(titleGeometry.largeTitleWeight, 0.5) * 2
headerTitle.alpha = CGFloat(sqrt(largeTitleOpacity))
blurView.contentView.subviews[1].alpha = CGFloat(sqrt(tinyTitleOpacity))
if let vfxSubview = blurView.subviews.first(where: {
String(describing: type(of: [=10=])) == "_UIVisualEffectSubview"
}) {
vfxSubview.backgroundColor = UIColor.systemBackground.withAlphaComponent(0)
}
if let vfxBackdrop = blurView.subviews.first(where: {
String(describing: type(of: [=10=])) == "_UIVisualEffectBackdropView"
}) {
vfxBackdrop.alpha = CGFloat(1 - sqrt(titleGeometry.largeTitleWeight))
}
var blurFrame = blurView.frame
var titleFrame = headerTitle.frame
blurFrame.origin.y = max(originalBlurRect.minY, originalBlurRect.minY + titleGeometry.blurOffset)
titleFrame.origin.y = originalTitleRect.minY + 364
blurView.frame = blurFrame
headerTitle.frame = titleFrame
headerView.scrollViewDidScroll(scrollView: scrollView)
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
1000
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell")
cell?.textLabel?.text = String(indexPath.row)
cell?.layer.zPosition = -1000
view.sendSubviewToBack(cell!)
view.bringSubviewToFront(tableView.tableHeaderView!)
return cell!
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.setNavigationBarHidden(true, animated: false)
}
func addTitle() {
let blurEffect = UIBlurEffect(style: UITraitCollection.current.userInterfaceStyle == .dark ? .dark : .light)
blurView = UIVisualEffectView(effect: blurEffect)
blurView.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.size.width, height: 44 + 38 + tableView.safeAreaInsets.top)
segmentedControl = UISegmentedControl(items: secondaryProfilePages)
segmentedControl.selectedSegmentIndex = 0
segmentedControl.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.size.width - 32, height: 30)
let scoreSize = ("+\(String(9999))" as NSString).boundingRect(with: CGSize(width: UIScreen.main.bounds.size.width, height: CGFloat.greatestFiniteMagnitude), options: [.truncatesLastVisibleLine, .usesLineFragmentOrigin], attributes: [NSAttributedString.Key.font: UIFont.preferredFont(forTextStyle: .caption1)], context: nil).size
scoreLabel = PaddingLabel(withInsets: 2.5, 2.5, 5, 5)
scoreLabel.text = "+\(String(9999))"
scoreLabel.font = UIFont.preferredFont(forTextStyle: .caption1)
scoreLabel.textColor = .black
scoreLabel.backgroundColor = .white
scoreLabel.layer.masksToBounds = true
scoreLabel.layer.cornerRadius = 5
let smallScoreLabel = PaddingLabel(withInsets: 2.5, 2.5, 5, 5)
smallScoreLabel.text = "+\(String(9999))"
smallScoreLabel.font = UIFont.preferredFont(forTextStyle: .caption1)
smallScoreLabel.textColor = .label
smallScoreLabel.backgroundColor = UITraitCollection.current.userInterfaceStyle == .dark ? .white : .black
smallScoreLabel.layer.masksToBounds = true
smallScoreLabel.layer.cornerRadius = 5
let nsText = "OmerFlame" as NSString?
let bigLabelSize = nsText?.boundingRect(with: CGSize(width: UIScreen.main.bounds.size.width - 32 - scoreLabel.intrinsicContentSize.width, height: CGFloat.greatestFiniteMagnitude), options: [.truncatesLastVisibleLine, .usesLineFragmentOrigin], attributes: [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 34, weight: .black)], context: nil).size
let smallLabelSize = nsText?.boundingRect(with: CGSize(width: UIScreen.main.bounds.size.width - 32 - scoreLabel.intrinsicContentSize.width, height: CGFloat.greatestFiniteMagnitude), options: [.truncatesLastVisibleLine, .usesLineFragmentOrigin], attributes: [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 18, weight: .bold)], context: nil).size
let largeLabel = UILabel(frame: CGRect(x: 0, y: 0, width: bigLabelSize!.width, height: bigLabelSize!.height))
let smallLabel = UILabel(frame: CGRect(x: 0, y: 0, width: smallLabelSize!.width, height: smallLabelSize!.height))
largeLabel.text = "OmerFlame"
largeLabel.font = .systemFont(ofSize: 34, weight: .black)
largeLabel.textColor = .white
largeLabel.adjustsFontSizeToFitWidth = true
largeLabel.minimumScaleFactor = 0.2
largeLabel.allowsDefaultTighteningForTruncation = true
largeLabel.numberOfLines = 1
smallLabel.text = "OmerFlame"
smallLabel.font = .systemFont(ofSize: 18, weight: .bold)
smallLabel.textColor = .label
smallLabel.adjustsFontSizeToFitWidth = true
smallLabel.minimumScaleFactor = 0.1
smallLabel.allowsDefaultTighteningForTruncation = true
smallLabel.numberOfLines = 1
largeLabel.translatesAutoresizingMaskIntoConstraints = false
smallLabel.translatesAutoresizingMaskIntoConstraints = false
headerTitle = UIStackView(frame: CGRect(x: 0, y: 0, width: largeLabel.frame.size.width + 5 + scoreLabel.intrinsicContentSize.width, height: max(largeLabel.frame.size.height, scoreLabel.intrinsicContentSize.height)))
headerTitle.axis = .horizontal
headerTitle.alignment = .center
headerTitle.distribution = .equalCentering
headerTitle.addArrangedSubview(largeLabel)
headerTitle.addArrangedSubview(scoreLabel)
let smallHeaderTitle = UIStackView(frame: CGRect(x: 0, y: 0, width: smallLabel.frame.size.width + 5 + smallScoreLabel.intrinsicContentSize.width, height: max(smallLabel.frame.size.height, smallScoreLabel.intrinsicContentSize.height)))
smallHeaderTitle.axis = .horizontal
smallHeaderTitle.alignment = .center
smallHeaderTitle.distribution = .equalCentering
smallHeaderTitle.addArrangedSubview(smallLabel)
smallHeaderTitle.addArrangedSubview(smallScoreLabel)
blurView.contentView.addSubview(headerTitle)
blurView.contentView.addSubview(smallHeaderTitle)
blurView.contentView.addSubview(segmentedControl)
tableView.tableHeaderView!.addSubview(blurView)
blurView.translatesAutoresizingMaskIntoConstraints = false
blurView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
blurView.heightAnchor.constraint(equalTo: tableView.tableHeaderView!.heightAnchor).isActive = true
blurView.widthAnchor.constraint(equalToConstant: UIScreen.main.bounds.size.width).isActive = true
largeLabel.translatesAutoresizingMaskIntoConstraints = false
smallLabel.translatesAutoresizingMaskIntoConstraints = false
scoreLabel.translatesAutoresizingMaskIntoConstraints = false
scoreLabel.leadingAnchor.constraint(equalTo: largeLabel.trailingAnchor, constant: 5).isActive = true
smallHeaderTitle.translatesAutoresizingMaskIntoConstraints = false
smallScoreLabel.translatesAutoresizingMaskIntoConstraints = false
smallScoreLabel.leadingAnchor.constraint(equalTo: smallLabel.trailingAnchor, constant: 5).isActive = true
smallScoreLabel.bottomAnchor.constraint(equalTo: smallLabel.bottomAnchor).isActive = true
smallHeaderTitle.centerXAnchor.constraint(equalTo: blurView.contentView.centerXAnchor).isActive = true
smallHeaderTitle.heightAnchor.constraint(equalToConstant: max(smallLabel.frame.size.height, smallScoreLabel.intrinsicContentSize.height)).isActive = true
smallHeaderTitle.widthAnchor.constraint(equalToConstant: smallLabel.frame.size.width + 5 + smallScoreLabel.intrinsicContentSize.width).isActive = true
smallHeaderTitle.bottomAnchor.constraint(equalTo: segmentedControl.topAnchor, constant: -4).isActive = true
headerTitle.translatesAutoresizingMaskIntoConstraints = false
headerTitle.bottomAnchor.constraint(equalTo: segmentedControl.topAnchor, constant: -8).isActive = true
headerTitle.widthAnchor.constraint(equalToConstant: largeLabel.frame.size.width + 5 + scoreLabel.intrinsicContentSize.width).isActive = true
headerTitle.heightAnchor.constraint(equalToConstant: max(largeLabel.frame.size.height, scoreLabel.intrinsicContentSize.height)).isActive = true
largeLabel.leadingAnchor.constraint(equalTo: blurView.contentView.leadingAnchor, constant: 16).isActive = true
originalBlurRect = blurView.frame
originalTitleRect = headerTitle.frame
segmentedControl.translatesAutoresizingMaskIntoConstraints = false
segmentedControl.bottomAnchor.constraint(equalTo: blurView.contentView.bottomAnchor, constant: -8).isActive = true
segmentedControl.widthAnchor.constraint(equalToConstant: UIScreen.main.bounds.size.width - 32).isActive = true
segmentedControl.heightAnchor.constraint(equalToConstant: 30).isActive = true
segmentedControl.leadingAnchor.constraint(equalTo: blurView.contentView.leadingAnchor, constant: 16).isActive = true
view.bringSubviewToFront(smallHeaderTitle)
view.bringSubviewToFront(segmentedControl)
segmentedControl.layer.zPosition = 1000
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
view.bringSubviewToFront(blurView)
}
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
if traitCollection.userInterfaceStyle == .dark {
(blurView.contentView.subviews[1] as! UIStackView).arrangedSubviews[1].backgroundColor = .white
((blurView.contentView.subviews[1] as! UIStackView).arrangedSubviews[1] as! UILabel).textColor = .black
} else {
(blurView.contentView.subviews[1] as! UIStackView).arrangedSubviews[1].backgroundColor = .black
((blurView.contentView.subviews[1] as! UIStackView).arrangedSubviews[1] as! UILabel).textColor = .white
}
}
}
extension TertiaryProfileScroll {
struct HeaderGeometry {
let width: CGFloat
let headerHeight: CGFloat
let elementsHeight: CGFloat
let headerOffset: CGFloat
let blurOffset: CGFloat
let elementsOffset: CGFloat
let largeTitleWeight: Double
}
func geometry(view: UIView, scrollView: UIScrollView) -> HeaderGeometry {
let safeArea = scrollView.safeAreaInsets
let minY = -(scrollView.contentOffset.y + scrollView.safeAreaInsets.top)
let hasScrolledUp = minY > 0
let hasScrolledToMinHeight = -minY >= 450 - 44 - safeArea.top
let headerHeight = hasScrolledUp ?
(tableView.tableHeaderView as! SecondaryStretchyTableHeaderView).containerView.frame.size.height + minY + 38 : (tableView.tableHeaderView as! SecondaryStretchyTableHeaderView).containerView.frame.size.height + 38
let elementsHeight = (tableView.tableHeaderView as! SecondaryStretchyTableHeaderView).frame.size.height + minY
let headerOffset: CGFloat
let blurOffset: CGFloat
let elementsOffset: CGFloat
let largeTitleWeight: Double
if hasScrolledUp {
headerOffset = -minY
blurOffset = -minY
elementsOffset = -minY
largeTitleWeight = 1
} else if hasScrolledToMinHeight {
headerOffset = -minY - 450 + 44 + safeArea.top
blurOffset = -minY - 450 + 44 + safeArea.top
elementsOffset = headerOffset / 2 - minY / 2
largeTitleWeight = 0
} else {
headerOffset = 0
blurOffset = 0
elementsOffset = -minY / 2
let difference = 450 - 44 - safeArea.top + minY
largeTitleWeight = difference <= 44 + 1 ? Double(difference / (44 + 1)) : 1
}
return HeaderGeometry(width: (tableView.tableHeaderView as! SecondaryStretchyTableHeaderView).frame.size.width, headerHeight: headerHeight, elementsHeight: elementsHeight, headerOffset: headerOffset, blurOffset: blurOffset, elementsOffset: elementsOffset, largeTitleWeight: largeTitleWeight)
}
}
class SecondaryStretchyTableHeaderView: UIView {
var imageContainerHeight = NSLayoutConstraint()
var imageContainerBottom = NSLayoutConstraint()
var imageViewHeight = NSLayoutConstraint()
var imageViewBottom = NSLayoutConstraint()
var imageViewTop = NSLayoutConstraint()
var containerView: UIView!
var imageContainer: UIView!
var imageView: UIImageView!
var largeTitleOpacity = Double()
var tinyTitleOpacity = Double()
var largeLabel: UILabel!
var tinyLabel: UILabel!
var containerViewHeight = NSLayoutConstraint()
var stack: UIStackView!
var title: StretchyHeaderTitle!
override init(frame: CGRect) {
super.init(frame: frame)
createViews()
setViewConstraints()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
func createViews() {
// Container View
containerView = UIView()
self.addSubview(containerView)
imageContainer = UIView()
imageContainer.backgroundColor = UIColor(hex: "d55161")
imageContainer.contentMode = .scaleAspectFill
imageContainer.clipsToBounds = true
containerView.addSubview(imageContainer)
// ImageView for background
imageView = UIImageView()
imageView.backgroundColor = UIColor(hex: "d55161")
imageView.contentMode = .scaleAspectFill
imageContainer.addSubview(imageView)
}
func setViewConstraints() {
// UIView Constraints
NSLayoutConstraint.activate([
self.widthAnchor.constraint(equalTo: containerView.widthAnchor),
self.centerXAnchor.constraint(equalTo: containerView.centerXAnchor),
self.heightAnchor.constraint(equalTo: containerView.heightAnchor)
])
// Container View Constraints
containerView.translatesAutoresizingMaskIntoConstraints = false
containerView.widthAnchor.constraint(equalTo: imageContainer.widthAnchor).isActive = true
containerViewHeight = containerView.heightAnchor.constraint(equalTo: self.heightAnchor)
containerViewHeight.isActive = true
// ImageView Constraints
imageContainer.translatesAutoresizingMaskIntoConstraints = false
imageContainerBottom = imageContainer.bottomAnchor.constraint(equalTo: containerView.bottomAnchor)
imageContainerBottom.isActive = true
imageContainerHeight = imageContainer.heightAnchor.constraint(equalTo: containerView.heightAnchor)
imageContainerHeight.isActive = true
imageView.translatesAutoresizingMaskIntoConstraints = false
imageViewBottom = imageView.bottomAnchor.constraint(equalTo: imageContainer.bottomAnchor, constant: -50)
imageViewBottom.isActive = true
imageViewTop = imageView.topAnchor.constraint(equalTo: imageContainer.topAnchor, constant: 50)
imageViewTop.isActive = true
imageView.centerXAnchor.constraint(equalTo: imageContainer.centerXAnchor).isActive = true
}
func scrollViewDidScroll(scrollView: UIScrollView) {
containerViewHeight.constant = scrollView.contentInset.top
let offsetY = -(scrollView.contentOffset.y + scrollView.contentInset.top)
containerView.clipsToBounds = offsetY <= 0
imageContainerBottom.constant = offsetY >= 0 ? 0 : -offsetY / 2
imageContainerHeight.constant = max(offsetY + scrollView.contentInset.top, scrollView.contentInset.top)
imageContainer.clipsToBounds = offsetY <= 0
imageViewBottom.constant = (offsetY >= 0 ? 0 : -offsetY / 2) - 50
imageViewTop.constant = (offsetY >= 0 ? 0 : -offsetY / 2) + 50
}
}
extension UIColor {
// MARK: - Initialization
convenience init?(hex: String) {
var hexSanitized = hex.trimmingCharacters(in: .whitespacesAndNewlines)
hexSanitized = hexSanitized.replacingOccurrences(of: "#", with: "")
var rgb: UInt32 = 0
var r: CGFloat = 0.0
var g: CGFloat = 0.0
var b: CGFloat = 0.0
var a: CGFloat = 1.0
let length = String(hexSanitized).count
guard Scanner(string: hexSanitized).scanHexInt32(&rgb) else { return nil }
if length == 6 {
r = CGFloat((rgb & 0xFF0000) >> 16) / 255.0
g = CGFloat((rgb & 0x00FF00) >> 8) / 255.0
b = CGFloat(rgb & 0x0000FF) / 255.0
} else if length == 8 {
r = CGFloat((rgb & 0xFF000000) >> 24) / 255.0
g = CGFloat((rgb & 0x00FF0000) >> 16) / 255.0
b = CGFloat((rgb & 0x0000FF00) >> 8) / 255.0
a = CGFloat(rgb & 0x000000FF) / 255.0
} else {
return nil
}
self.init(red: r, green: g, blue: b, alpha: a)
}
// MARK: - Computed Properties
var toHex: String? {
return toHex()
}
// MARK: - From UIColor to String
func toHex(alpha: Bool = false) -> String? {
guard let components = cgColor.components, components.count >= 3 else {
return nil
}
let r = Float(components[0])
let g = Float(components[1])
let b = Float(components[2])
var a = Float(1.0)
if components.count >= 4 {
a = Float(components[3])
}
if alpha {
return String(format: "%02lX%02lX%02lX%02lX", lroundf(r * 255), lroundf(g * 255), lroundf(b * 255), lroundf(a * 255))
} else {
return String(format: "%02lX%02lX%02lX", lroundf(r * 255), lroundf(g * 255), lroundf(b * 255))
}
}
}
我想我提供了与我的问题相关的所有代码。如果我错过了什么,请告诉我。我知道代码真的很长,但我完全不知道我应该做什么。欢迎任何回复!对于可能重复的代码,我也深表歉意,我正在匆忙发布。
谢谢!
好的 - 我收到了你的密码 运行。
问题是您的分段控件 延伸到 table header 视图的边界 之外。
我认为将属于“弹性 header” 的所有 UI 元素保留在 中更有意义=52=] class,所以这不是我推荐的方式,但是这 应该 给你你的分段控制交互:
在你的 SecondaryStretchyTableHeaderView
class 中,添加这个 var / 属性:
weak var segControl: UISegmentedControl?
在你的 TertiaryProfileScroll
class 中的 addTitle()
中,添加:
// your existing code
blurView.contentView.addSubview(headerTitle)
blurView.contentView.addSubview(smallHeaderTitle)
blurView.contentView.addSubview(segmentedControl)
tableView.tableHeaderView!.addSubview(blurView)
// add this
if let v = tableView.tableHeaderView as? SecondaryStretchyTableHeaderView {
// give our custom header view a reference to the segemented control
v.segControl = segmentedControl
}
返回您的 SecondaryStretchyTableHeaderView
class,添加此函数:
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
guard isUserInteractionEnabled,
!isHidden,
alpha >= 0.01,
let sc = segControl
else { return nil }
// if we tap outside the bounds,
// but on the segmented control
// return the segmented control
let convertedPoint = sc.convert(point, from: self)
if let v = sc.hitTest(convertedPoint, with: event) {
return v
}
guard self.point(inside: point, with: event) else { return nil }
return self
}
这将允许您与分段控件进行交互,即使它在 table header 视图的范围之外。
附带说明一下,您似乎在不需要的地方设置 .layer.zPosition
。在 cellForRowAt
中,我注释掉了这些行:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell")
cell?.textLabel?.text = String(indexPath.row)
//cell?.layer.zPosition = -1000
//view.sendSubviewToBack(cell!)
//view.bringSubviewToFront(tableView.tableHeaderView!)
return cell!
}
并且还注释掉了这一行(在addTitle()
的末尾):
//segmentedControl.layer.zPosition = 1000
我看不出有什么不同。
我的问题非常具体,我必须为此提供代码,抱歉,如果代码太复杂或太复杂,我对 UIKit 有点陌生,因为我更多地学习 SwiftUI 而不是 UIKit。
我有一个非常复杂的 UITableView
header,有一些不错的动画等等,我在上面放了一个 UISegmentedControl
。但是,一旦 UITableViewCell
落后于 UISegmentedControl
,它就会停止工作,并且 UISegmentedControl
.
如果没有视觉上下文和代码,这很难解释,所以就在这里。
如您所见,在顶部,用户可以自由地与 UISegmentedControl 交互,但是当我开始滚动并且 table 视图单元格在 UISegmentedControl
后面时(正如预期的那样),分段控件完全停止响应用户触摸输入。
再次,请原谅我的长(而且可能是坏的)代码,我还在学习。
这是我为 UITableView 编写的代码:
class TertiaryProfileScroll: UITableViewController {
var segmentedControl: UISegmentedControl!
var testBlurView: UIVisualEffectView!
var headerTitle: UIStackView!
var blurView: UIVisualEffectView!
var scoreRect: UIView!
var scoreLabel: UILabel!
var originalBlurRect: CGRect!
var originalTitleRect: CGRect!
var originalTestRect: CGRect!
override func viewDidLoad() {
super.viewDidLoad()
let headerView = SecondaryStretchyTableHeaderView(frame: CGRect(x: 0, y: 0, width: view.bounds.width, height: 488))
let image = UIImage(named: "background_image")
UIGraphicsBeginImageContextWithOptions(CGSize(width: 350, height: 350), false, 4)
image!.draw(in: CGRect(origin: CGPoint(x: 0, y: 0), size: CGSize(width: 350, height: 350)))
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
tableView.allowsSelection = false
tableView.separatorStyle = .none
headerView.imageView.image = newImage
tableView.tableHeaderView = headerView
addTitle()
blurView.contentView.isUserInteractionEnabled = true
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
}
override func scrollViewDidScroll(_ scrollView: UIScrollView) {
guard tableView.tableHeaderView != nil else { return }
let headerView = self.tableView.tableHeaderView as! SecondaryStretchyTableHeaderView
let headerGeometry = self.geometry(view: headerView, scrollView: scrollView)
let titleGeometry = self.geometry(view: headerTitle, scrollView: scrollView)
(tableView.tableHeaderView as! SecondaryStretchyTableHeaderView).containerView
.alpha = CGFloat(sqrt(headerGeometry.largeTitleWeight))
(tableView.tableHeaderView as! SecondaryStretchyTableHeaderView).imageContainer.alpha = CGFloat(sqrt(headerGeometry.largeTitleWeight))
let largeTitleOpacity = (max(titleGeometry.largeTitleWeight, 0.5) - 0.5) * 2
let tinyTitleOpacity = 1 - min(titleGeometry.largeTitleWeight, 0.5) * 2
headerTitle.alpha = CGFloat(sqrt(largeTitleOpacity))
blurView.contentView.subviews[1].alpha = CGFloat(sqrt(tinyTitleOpacity))
if let vfxSubview = blurView.subviews.first(where: {
String(describing: type(of: [=10=])) == "_UIVisualEffectSubview"
}) {
vfxSubview.backgroundColor = UIColor.systemBackground.withAlphaComponent(0)
}
if let vfxBackdrop = blurView.subviews.first(where: {
String(describing: type(of: [=10=])) == "_UIVisualEffectBackdropView"
}) {
vfxBackdrop.alpha = CGFloat(1 - sqrt(titleGeometry.largeTitleWeight))
}
var blurFrame = blurView.frame
var titleFrame = headerTitle.frame
blurFrame.origin.y = max(originalBlurRect.minY, originalBlurRect.minY + titleGeometry.blurOffset)
titleFrame.origin.y = originalTitleRect.minY + 364
blurView.frame = blurFrame
headerTitle.frame = titleFrame
headerView.scrollViewDidScroll(scrollView: scrollView)
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
1000
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell")
cell?.textLabel?.text = String(indexPath.row)
cell?.layer.zPosition = -1000
view.sendSubviewToBack(cell!)
view.bringSubviewToFront(tableView.tableHeaderView!)
return cell!
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.setNavigationBarHidden(true, animated: false)
}
func addTitle() {
let blurEffect = UIBlurEffect(style: UITraitCollection.current.userInterfaceStyle == .dark ? .dark : .light)
blurView = UIVisualEffectView(effect: blurEffect)
blurView.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.size.width, height: 44 + 38 + tableView.safeAreaInsets.top)
segmentedControl = UISegmentedControl(items: secondaryProfilePages)
segmentedControl.selectedSegmentIndex = 0
segmentedControl.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.size.width - 32, height: 30)
let scoreSize = ("+\(String(9999))" as NSString).boundingRect(with: CGSize(width: UIScreen.main.bounds.size.width, height: CGFloat.greatestFiniteMagnitude), options: [.truncatesLastVisibleLine, .usesLineFragmentOrigin], attributes: [NSAttributedString.Key.font: UIFont.preferredFont(forTextStyle: .caption1)], context: nil).size
scoreLabel = PaddingLabel(withInsets: 2.5, 2.5, 5, 5)
scoreLabel.text = "+\(String(9999))"
scoreLabel.font = UIFont.preferredFont(forTextStyle: .caption1)
scoreLabel.textColor = .black
scoreLabel.backgroundColor = .white
scoreLabel.layer.masksToBounds = true
scoreLabel.layer.cornerRadius = 5
let smallScoreLabel = PaddingLabel(withInsets: 2.5, 2.5, 5, 5)
smallScoreLabel.text = "+\(String(9999))"
smallScoreLabel.font = UIFont.preferredFont(forTextStyle: .caption1)
smallScoreLabel.textColor = .label
smallScoreLabel.backgroundColor = UITraitCollection.current.userInterfaceStyle == .dark ? .white : .black
smallScoreLabel.layer.masksToBounds = true
smallScoreLabel.layer.cornerRadius = 5
let nsText = "OmerFlame" as NSString?
let bigLabelSize = nsText?.boundingRect(with: CGSize(width: UIScreen.main.bounds.size.width - 32 - scoreLabel.intrinsicContentSize.width, height: CGFloat.greatestFiniteMagnitude), options: [.truncatesLastVisibleLine, .usesLineFragmentOrigin], attributes: [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 34, weight: .black)], context: nil).size
let smallLabelSize = nsText?.boundingRect(with: CGSize(width: UIScreen.main.bounds.size.width - 32 - scoreLabel.intrinsicContentSize.width, height: CGFloat.greatestFiniteMagnitude), options: [.truncatesLastVisibleLine, .usesLineFragmentOrigin], attributes: [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 18, weight: .bold)], context: nil).size
let largeLabel = UILabel(frame: CGRect(x: 0, y: 0, width: bigLabelSize!.width, height: bigLabelSize!.height))
let smallLabel = UILabel(frame: CGRect(x: 0, y: 0, width: smallLabelSize!.width, height: smallLabelSize!.height))
largeLabel.text = "OmerFlame"
largeLabel.font = .systemFont(ofSize: 34, weight: .black)
largeLabel.textColor = .white
largeLabel.adjustsFontSizeToFitWidth = true
largeLabel.minimumScaleFactor = 0.2
largeLabel.allowsDefaultTighteningForTruncation = true
largeLabel.numberOfLines = 1
smallLabel.text = "OmerFlame"
smallLabel.font = .systemFont(ofSize: 18, weight: .bold)
smallLabel.textColor = .label
smallLabel.adjustsFontSizeToFitWidth = true
smallLabel.minimumScaleFactor = 0.1
smallLabel.allowsDefaultTighteningForTruncation = true
smallLabel.numberOfLines = 1
largeLabel.translatesAutoresizingMaskIntoConstraints = false
smallLabel.translatesAutoresizingMaskIntoConstraints = false
headerTitle = UIStackView(frame: CGRect(x: 0, y: 0, width: largeLabel.frame.size.width + 5 + scoreLabel.intrinsicContentSize.width, height: max(largeLabel.frame.size.height, scoreLabel.intrinsicContentSize.height)))
headerTitle.axis = .horizontal
headerTitle.alignment = .center
headerTitle.distribution = .equalCentering
headerTitle.addArrangedSubview(largeLabel)
headerTitle.addArrangedSubview(scoreLabel)
let smallHeaderTitle = UIStackView(frame: CGRect(x: 0, y: 0, width: smallLabel.frame.size.width + 5 + smallScoreLabel.intrinsicContentSize.width, height: max(smallLabel.frame.size.height, smallScoreLabel.intrinsicContentSize.height)))
smallHeaderTitle.axis = .horizontal
smallHeaderTitle.alignment = .center
smallHeaderTitle.distribution = .equalCentering
smallHeaderTitle.addArrangedSubview(smallLabel)
smallHeaderTitle.addArrangedSubview(smallScoreLabel)
blurView.contentView.addSubview(headerTitle)
blurView.contentView.addSubview(smallHeaderTitle)
blurView.contentView.addSubview(segmentedControl)
tableView.tableHeaderView!.addSubview(blurView)
blurView.translatesAutoresizingMaskIntoConstraints = false
blurView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
blurView.heightAnchor.constraint(equalTo: tableView.tableHeaderView!.heightAnchor).isActive = true
blurView.widthAnchor.constraint(equalToConstant: UIScreen.main.bounds.size.width).isActive = true
largeLabel.translatesAutoresizingMaskIntoConstraints = false
smallLabel.translatesAutoresizingMaskIntoConstraints = false
scoreLabel.translatesAutoresizingMaskIntoConstraints = false
scoreLabel.leadingAnchor.constraint(equalTo: largeLabel.trailingAnchor, constant: 5).isActive = true
smallHeaderTitle.translatesAutoresizingMaskIntoConstraints = false
smallScoreLabel.translatesAutoresizingMaskIntoConstraints = false
smallScoreLabel.leadingAnchor.constraint(equalTo: smallLabel.trailingAnchor, constant: 5).isActive = true
smallScoreLabel.bottomAnchor.constraint(equalTo: smallLabel.bottomAnchor).isActive = true
smallHeaderTitle.centerXAnchor.constraint(equalTo: blurView.contentView.centerXAnchor).isActive = true
smallHeaderTitle.heightAnchor.constraint(equalToConstant: max(smallLabel.frame.size.height, smallScoreLabel.intrinsicContentSize.height)).isActive = true
smallHeaderTitle.widthAnchor.constraint(equalToConstant: smallLabel.frame.size.width + 5 + smallScoreLabel.intrinsicContentSize.width).isActive = true
smallHeaderTitle.bottomAnchor.constraint(equalTo: segmentedControl.topAnchor, constant: -4).isActive = true
headerTitle.translatesAutoresizingMaskIntoConstraints = false
headerTitle.bottomAnchor.constraint(equalTo: segmentedControl.topAnchor, constant: -8).isActive = true
headerTitle.widthAnchor.constraint(equalToConstant: largeLabel.frame.size.width + 5 + scoreLabel.intrinsicContentSize.width).isActive = true
headerTitle.heightAnchor.constraint(equalToConstant: max(largeLabel.frame.size.height, scoreLabel.intrinsicContentSize.height)).isActive = true
largeLabel.leadingAnchor.constraint(equalTo: blurView.contentView.leadingAnchor, constant: 16).isActive = true
originalBlurRect = blurView.frame
originalTitleRect = headerTitle.frame
segmentedControl.translatesAutoresizingMaskIntoConstraints = false
segmentedControl.bottomAnchor.constraint(equalTo: blurView.contentView.bottomAnchor, constant: -8).isActive = true
segmentedControl.widthAnchor.constraint(equalToConstant: UIScreen.main.bounds.size.width - 32).isActive = true
segmentedControl.heightAnchor.constraint(equalToConstant: 30).isActive = true
segmentedControl.leadingAnchor.constraint(equalTo: blurView.contentView.leadingAnchor, constant: 16).isActive = true
view.bringSubviewToFront(smallHeaderTitle)
view.bringSubviewToFront(segmentedControl)
segmentedControl.layer.zPosition = 1000
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
view.bringSubviewToFront(blurView)
}
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
if traitCollection.userInterfaceStyle == .dark {
(blurView.contentView.subviews[1] as! UIStackView).arrangedSubviews[1].backgroundColor = .white
((blurView.contentView.subviews[1] as! UIStackView).arrangedSubviews[1] as! UILabel).textColor = .black
} else {
(blurView.contentView.subviews[1] as! UIStackView).arrangedSubviews[1].backgroundColor = .black
((blurView.contentView.subviews[1] as! UIStackView).arrangedSubviews[1] as! UILabel).textColor = .white
}
}
}
extension TertiaryProfileScroll {
struct HeaderGeometry {
let width: CGFloat
let headerHeight: CGFloat
let elementsHeight: CGFloat
let headerOffset: CGFloat
let blurOffset: CGFloat
let elementsOffset: CGFloat
let largeTitleWeight: Double
}
func geometry(view: UIView, scrollView: UIScrollView) -> HeaderGeometry {
let safeArea = scrollView.safeAreaInsets
let minY = -(scrollView.contentOffset.y + scrollView.safeAreaInsets.top)
let hasScrolledUp = minY > 0
let hasScrolledToMinHeight = -minY >= 450 - 44 - safeArea.top
let headerHeight = hasScrolledUp ?
(tableView.tableHeaderView as! SecondaryStretchyTableHeaderView).containerView.frame.size.height + minY + 38 : (tableView.tableHeaderView as! SecondaryStretchyTableHeaderView).containerView.frame.size.height + 38
let elementsHeight = (tableView.tableHeaderView as! SecondaryStretchyTableHeaderView).frame.size.height + minY
let headerOffset: CGFloat
let blurOffset: CGFloat
let elementsOffset: CGFloat
let largeTitleWeight: Double
if hasScrolledUp {
headerOffset = -minY
blurOffset = -minY
elementsOffset = -minY
largeTitleWeight = 1
} else if hasScrolledToMinHeight {
headerOffset = -minY - 450 + 44 + safeArea.top
blurOffset = -minY - 450 + 44 + safeArea.top
elementsOffset = headerOffset / 2 - minY / 2
largeTitleWeight = 0
} else {
headerOffset = 0
blurOffset = 0
elementsOffset = -minY / 2
let difference = 450 - 44 - safeArea.top + minY
largeTitleWeight = difference <= 44 + 1 ? Double(difference / (44 + 1)) : 1
}
return HeaderGeometry(width: (tableView.tableHeaderView as! SecondaryStretchyTableHeaderView).frame.size.width, headerHeight: headerHeight, elementsHeight: elementsHeight, headerOffset: headerOffset, blurOffset: blurOffset, elementsOffset: elementsOffset, largeTitleWeight: largeTitleWeight)
}
}
class SecondaryStretchyTableHeaderView: UIView {
var imageContainerHeight = NSLayoutConstraint()
var imageContainerBottom = NSLayoutConstraint()
var imageViewHeight = NSLayoutConstraint()
var imageViewBottom = NSLayoutConstraint()
var imageViewTop = NSLayoutConstraint()
var containerView: UIView!
var imageContainer: UIView!
var imageView: UIImageView!
var largeTitleOpacity = Double()
var tinyTitleOpacity = Double()
var largeLabel: UILabel!
var tinyLabel: UILabel!
var containerViewHeight = NSLayoutConstraint()
var stack: UIStackView!
var title: StretchyHeaderTitle!
override init(frame: CGRect) {
super.init(frame: frame)
createViews()
setViewConstraints()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
func createViews() {
// Container View
containerView = UIView()
self.addSubview(containerView)
imageContainer = UIView()
imageContainer.backgroundColor = UIColor(hex: "d55161")
imageContainer.contentMode = .scaleAspectFill
imageContainer.clipsToBounds = true
containerView.addSubview(imageContainer)
// ImageView for background
imageView = UIImageView()
imageView.backgroundColor = UIColor(hex: "d55161")
imageView.contentMode = .scaleAspectFill
imageContainer.addSubview(imageView)
}
func setViewConstraints() {
// UIView Constraints
NSLayoutConstraint.activate([
self.widthAnchor.constraint(equalTo: containerView.widthAnchor),
self.centerXAnchor.constraint(equalTo: containerView.centerXAnchor),
self.heightAnchor.constraint(equalTo: containerView.heightAnchor)
])
// Container View Constraints
containerView.translatesAutoresizingMaskIntoConstraints = false
containerView.widthAnchor.constraint(equalTo: imageContainer.widthAnchor).isActive = true
containerViewHeight = containerView.heightAnchor.constraint(equalTo: self.heightAnchor)
containerViewHeight.isActive = true
// ImageView Constraints
imageContainer.translatesAutoresizingMaskIntoConstraints = false
imageContainerBottom = imageContainer.bottomAnchor.constraint(equalTo: containerView.bottomAnchor)
imageContainerBottom.isActive = true
imageContainerHeight = imageContainer.heightAnchor.constraint(equalTo: containerView.heightAnchor)
imageContainerHeight.isActive = true
imageView.translatesAutoresizingMaskIntoConstraints = false
imageViewBottom = imageView.bottomAnchor.constraint(equalTo: imageContainer.bottomAnchor, constant: -50)
imageViewBottom.isActive = true
imageViewTop = imageView.topAnchor.constraint(equalTo: imageContainer.topAnchor, constant: 50)
imageViewTop.isActive = true
imageView.centerXAnchor.constraint(equalTo: imageContainer.centerXAnchor).isActive = true
}
func scrollViewDidScroll(scrollView: UIScrollView) {
containerViewHeight.constant = scrollView.contentInset.top
let offsetY = -(scrollView.contentOffset.y + scrollView.contentInset.top)
containerView.clipsToBounds = offsetY <= 0
imageContainerBottom.constant = offsetY >= 0 ? 0 : -offsetY / 2
imageContainerHeight.constant = max(offsetY + scrollView.contentInset.top, scrollView.contentInset.top)
imageContainer.clipsToBounds = offsetY <= 0
imageViewBottom.constant = (offsetY >= 0 ? 0 : -offsetY / 2) - 50
imageViewTop.constant = (offsetY >= 0 ? 0 : -offsetY / 2) + 50
}
}
extension UIColor {
// MARK: - Initialization
convenience init?(hex: String) {
var hexSanitized = hex.trimmingCharacters(in: .whitespacesAndNewlines)
hexSanitized = hexSanitized.replacingOccurrences(of: "#", with: "")
var rgb: UInt32 = 0
var r: CGFloat = 0.0
var g: CGFloat = 0.0
var b: CGFloat = 0.0
var a: CGFloat = 1.0
let length = String(hexSanitized).count
guard Scanner(string: hexSanitized).scanHexInt32(&rgb) else { return nil }
if length == 6 {
r = CGFloat((rgb & 0xFF0000) >> 16) / 255.0
g = CGFloat((rgb & 0x00FF00) >> 8) / 255.0
b = CGFloat(rgb & 0x0000FF) / 255.0
} else if length == 8 {
r = CGFloat((rgb & 0xFF000000) >> 24) / 255.0
g = CGFloat((rgb & 0x00FF0000) >> 16) / 255.0
b = CGFloat((rgb & 0x0000FF00) >> 8) / 255.0
a = CGFloat(rgb & 0x000000FF) / 255.0
} else {
return nil
}
self.init(red: r, green: g, blue: b, alpha: a)
}
// MARK: - Computed Properties
var toHex: String? {
return toHex()
}
// MARK: - From UIColor to String
func toHex(alpha: Bool = false) -> String? {
guard let components = cgColor.components, components.count >= 3 else {
return nil
}
let r = Float(components[0])
let g = Float(components[1])
let b = Float(components[2])
var a = Float(1.0)
if components.count >= 4 {
a = Float(components[3])
}
if alpha {
return String(format: "%02lX%02lX%02lX%02lX", lroundf(r * 255), lroundf(g * 255), lroundf(b * 255), lroundf(a * 255))
} else {
return String(format: "%02lX%02lX%02lX", lroundf(r * 255), lroundf(g * 255), lroundf(b * 255))
}
}
}
我想我提供了与我的问题相关的所有代码。如果我错过了什么,请告诉我。我知道代码真的很长,但我完全不知道我应该做什么。欢迎任何回复!对于可能重复的代码,我也深表歉意,我正在匆忙发布。
谢谢!
好的 - 我收到了你的密码 运行。
问题是您的分段控件 延伸到 table header 视图的边界 之外。
我认为将属于“弹性 header” 的所有 UI 元素保留在 中更有意义=52=] class,所以这不是我推荐的方式,但是这 应该 给你你的分段控制交互:
在你的 SecondaryStretchyTableHeaderView
class 中,添加这个 var / 属性:
weak var segControl: UISegmentedControl?
在你的 TertiaryProfileScroll
class 中的 addTitle()
中,添加:
// your existing code
blurView.contentView.addSubview(headerTitle)
blurView.contentView.addSubview(smallHeaderTitle)
blurView.contentView.addSubview(segmentedControl)
tableView.tableHeaderView!.addSubview(blurView)
// add this
if let v = tableView.tableHeaderView as? SecondaryStretchyTableHeaderView {
// give our custom header view a reference to the segemented control
v.segControl = segmentedControl
}
返回您的 SecondaryStretchyTableHeaderView
class,添加此函数:
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
guard isUserInteractionEnabled,
!isHidden,
alpha >= 0.01,
let sc = segControl
else { return nil }
// if we tap outside the bounds,
// but on the segmented control
// return the segmented control
let convertedPoint = sc.convert(point, from: self)
if let v = sc.hitTest(convertedPoint, with: event) {
return v
}
guard self.point(inside: point, with: event) else { return nil }
return self
}
这将允许您与分段控件进行交互,即使它在 table header 视图的范围之外。
附带说明一下,您似乎在不需要的地方设置 .layer.zPosition
。在 cellForRowAt
中,我注释掉了这些行:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell")
cell?.textLabel?.text = String(indexPath.row)
//cell?.layer.zPosition = -1000
//view.sendSubviewToBack(cell!)
//view.bringSubviewToFront(tableView.tableHeaderView!)
return cell!
}
并且还注释掉了这一行(在addTitle()
的末尾):
//segmentedControl.layer.zPosition = 1000
我看不出有什么不同。