Ios 如何将UIView置于其他UIView之上?

Ios 如何将UIView置于其他UIView之上?,ios,swift,uistackview,Ios,Swift,Uistackview,现在我有一个collectionView,其中每个单元格都包含一个水平堆栈视图。stackView由一系列UIView(矩形)填充,每个月的每一天一个,每个单元格对应一个月。我按如下方式填充堆栈视图: func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { if collectionView ==

现在我有一个collectionView,其中每个单元格都包含一个水平堆栈视图。stackView由一系列UIView(矩形)填充,每个月的每一天一个,每个单元格对应一个月。我按如下方式填充堆栈视图:

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        if collectionView == self.collectionView {
            ...
            return cell
        } else if collectionView == self.timeline {
            let index = indexPath.row
            let dateFormatter = DateFormatter()
            dateFormatter.dateFormat = "MMM"
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: timelineMonthCellReuseIdentifier, for: indexPath) as! SNTimelineMonthViewCell
            let firstPost = posts.first?.timeStamp
            let month = Calendar.current.date(byAdding: .month, value: index, to: firstPost!)
            print(dateFormatter.string(from: month!),dateFormatter.string(from: firstPost!),"month diff")
            for post in posts {
                print(post.timeStamp, "month diff")
            }
            cell.monthLabel.text = dateFormatter.string(from: month!)
            cell.monthLabel.textAlignment = .center

            if let start = month?.startOfMonth(), let end = month?.endOfMonth(), let stackView = cell.dayTicks {
                var date = start
                while date <= end {
                    let line = UIView()
                    if posts.contains(where: { Calendar.current.isDate(date, inSameDayAs: $0.timeStamp) }) {
                        line.backgroundColor = UIColor(red:0.15, green:0.67, blue:0.93, alpha:1.0)
                        let tapGuesture = UITapGestureRecognizer(target: self, action:  #selector (self.tapBar (_:)))
                        line.isUserInteractionEnabled = true
                        line.addGestureRecognizer(tapGuesture)
                        self.dayTicks[date] = line
                    } else {
                        line.backgroundColor = UIColor.clear
                    }
                    stackView.addArrangedSubview(line)
                    date = Calendar.current.date(byAdding: .day, value: 1, to: date)!
                }
            }
            return cell
        } else {
            preconditionFailure("Unknown collection view!")
        }
    }
这种方法很有效,看起来是这样的:

func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        if scrollView == self.collectionView {
            print("is collection view")
            print(scrollView,"collection view")
            let currentIndex = self.collectionView.contentOffset.x / self.collectionView.frame.size.width
            let post = posts[Int(currentIndex)]
            for (_,tick) in self.dayTicks {
                tick.layer.sublayers = nil
            }
            let day = Calendar.current.startOfDay(for: post.timeStamp)
            let tick = self.dayTicks[day]
            let arrowLayer = CAShapeLayer()
            let path = UIBezierPath()
            let start_x = (tick?.bounds.origin.x)!
            let start_y = (tick?.bounds.minY)!
            let top_width = (tick?.bounds.width)!
            let tick_height = (tick?.bounds.height)!
            let tip_height = CGFloat(10)
            let tip_flare = CGFloat(10)
            path.move(to: CGPoint(x: start_x, y: start_y))
            path.addLine(to: CGPoint(x: start_x + top_width,y: start_y))
            path.addLine(to: CGPoint(x: start_x + top_width,y: start_y + tick_height))
            path.addLine(to: CGPoint(x: start_x + top_width + tip_flare,y: start_y+tick_height+tip_height))
            path.addLine(to: CGPoint(x: start_x - tip_flare,y: start_y + tick_height + tip_height))
            path.addLine(to: CGPoint(x: start_x,y: start_y+tick_height))
            path.close()
            arrowLayer.path = path.cgPath
            arrowLayer.fillColor = UIColor(red:0.99, green:0.13, blue:0.25, alpha:1.0).cgColor
            tick?.layer.addSublayer(arrowLayer)         
        } else {
            print(scrollView, "timeline collection view")
        }


    }

添加了红色矩形,但它显示在dayTick的右侧,并显示为一个细长矩形。实际上,引用的跟踪器针图像如下所示:

func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        if scrollView == self.collectionView {
            print("is collection view")
            print(scrollView,"collection view")
            let currentIndex = self.collectionView.contentOffset.x / self.collectionView.frame.size.width
            let post = posts[Int(currentIndex)]
            for (_,tick) in self.dayTicks {
                tick.layer.sublayers = nil
            }
            let day = Calendar.current.startOfDay(for: post.timeStamp)
            let tick = self.dayTicks[day]
            let arrowLayer = CAShapeLayer()
            let path = UIBezierPath()
            let start_x = (tick?.bounds.origin.x)!
            let start_y = (tick?.bounds.minY)!
            let top_width = (tick?.bounds.width)!
            let tick_height = (tick?.bounds.height)!
            let tip_height = CGFloat(10)
            let tip_flare = CGFloat(10)
            path.move(to: CGPoint(x: start_x, y: start_y))
            path.addLine(to: CGPoint(x: start_x + top_width,y: start_y))
            path.addLine(to: CGPoint(x: start_x + top_width,y: start_y + tick_height))
            path.addLine(to: CGPoint(x: start_x + top_width + tip_flare,y: start_y+tick_height+tip_height))
            path.addLine(to: CGPoint(x: start_x - tip_flare,y: start_y + tick_height + tip_height))
            path.addLine(to: CGPoint(x: start_x,y: start_y+tick_height))
            path.close()
            arrowLayer.path = path.cgPath
            arrowLayer.fillColor = UIColor(red:0.99, green:0.13, blue:0.25, alpha:1.0).cgColor
            tick?.layer.addSublayer(arrowLayer)         
        } else {
            print(scrollView, "timeline collection view")
        }


    }

这至少是红色的来源,但正如你所看到的那样,它将红色拉伸到了一个奇怪的位置,并将所有不在矩形UIView空间中的东西剪短

现在请注意,我注释掉了设置箭头视图的大小和原点以及将clipToBounds设置为false的4行。当我取消注释这些行时,箭头视图根本不显示,所以我一定是做错了。我想展示的是这样的东西:

func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        if scrollView == self.collectionView {
            print("is collection view")
            print(scrollView,"collection view")
            let currentIndex = self.collectionView.contentOffset.x / self.collectionView.frame.size.width
            let post = posts[Int(currentIndex)]
            for (_,tick) in self.dayTicks {
                tick.layer.sublayers = nil
            }
            let day = Calendar.current.startOfDay(for: post.timeStamp)
            let tick = self.dayTicks[day]
            let arrowLayer = CAShapeLayer()
            let path = UIBezierPath()
            let start_x = (tick?.bounds.origin.x)!
            let start_y = (tick?.bounds.minY)!
            let top_width = (tick?.bounds.width)!
            let tick_height = (tick?.bounds.height)!
            let tip_height = CGFloat(10)
            let tip_flare = CGFloat(10)
            path.move(to: CGPoint(x: start_x, y: start_y))
            path.addLine(to: CGPoint(x: start_x + top_width,y: start_y))
            path.addLine(to: CGPoint(x: start_x + top_width,y: start_y + tick_height))
            path.addLine(to: CGPoint(x: start_x + top_width + tip_flare,y: start_y+tick_height+tip_height))
            path.addLine(to: CGPoint(x: start_x - tip_flare,y: start_y + tick_height + tip_height))
            path.addLine(to: CGPoint(x: start_x,y: start_y+tick_height))
            path.close()
            arrowLayer.path = path.cgPath
            arrowLayer.fillColor = UIColor(red:0.99, green:0.13, blue:0.25, alpha:1.0).cgColor
            tick?.layer.addSublayer(arrowLayer)         
        } else {
            print(scrollView, "timeline collection view")
        }


    }


我怎么能把它直接放在上面呢

看起来箭头视图上有一个固定的高度。可能是红色三角形部分在另一个视图下吗


单击调试视图层次结构,这是右起第二个图标-它看起来像3个矩形。检查是否整个图像都在那里。

另一个透视图可能是使用CALayer进行此操作。以下是一些帮助您发现解决方案的线索(摘自另一个项目):

@IBInspectable open var slideIndicatorThickness: CGFloat = 5.0 {
    didSet {
        if slideIndicator != nil { slideIndicator.removeFromSuperlayer() }
        let slideLayer = CALayer()
        let theOrigin = CGPoint(x: bounds.origin.x, y: bounds.origin.y)
        let theSize = CGSize(width: CGFloat(3.0), height: CGFloat(10.0)
        slideLayer.frame = CGRect(origin: theOrigin, size: theSize)
        slideLayer.backgroundColor = UIColor.orange.cgColor
        slideIndicator = slideLayer
        layer.addSublayer(slideIndicator)
    }
}

fileprivate var slideIndicator: CALayer!

fileprivate func updateIndicator() {
    // ..
    // Somehow figure out new frame, based on stack view's frame.
    slideIndicator.frame.origin.x = newOrigin
}

您可能必须在UIStackView的子类或您自己的自定义视图(UIStackView的包装器)上实现这一点。

我最终使用了CALayer-由于@ouni的建议,出于某种原因,CALayer似乎直接在视图顶部绘制,而子视图没有。一个关键点是取消选中collectionView单元格本身(与子视图相反)上的“剪辑到边界”框,以便我可以在collection view单元格外绘制箭头的扩底:

我的代码如下所示:

func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        if scrollView == self.collectionView {
            print("is collection view")
            print(scrollView,"collection view")
            let currentIndex = self.collectionView.contentOffset.x / self.collectionView.frame.size.width
            let post = posts[Int(currentIndex)]
            for (_,tick) in self.dayTicks {
                tick.layer.sublayers = nil
            }
            let day = Calendar.current.startOfDay(for: post.timeStamp)
            let tick = self.dayTicks[day]
            let arrowLayer = CAShapeLayer()
            let path = UIBezierPath()
            let start_x = (tick?.bounds.origin.x)!
            let start_y = (tick?.bounds.minY)!
            let top_width = (tick?.bounds.width)!
            let tick_height = (tick?.bounds.height)!
            let tip_height = CGFloat(10)
            let tip_flare = CGFloat(10)
            path.move(to: CGPoint(x: start_x, y: start_y))
            path.addLine(to: CGPoint(x: start_x + top_width,y: start_y))
            path.addLine(to: CGPoint(x: start_x + top_width,y: start_y + tick_height))
            path.addLine(to: CGPoint(x: start_x + top_width + tip_flare,y: start_y+tick_height+tip_height))
            path.addLine(to: CGPoint(x: start_x - tip_flare,y: start_y + tick_height + tip_height))
            path.addLine(to: CGPoint(x: start_x,y: start_y+tick_height))
            path.close()
            arrowLayer.path = path.cgPath
            arrowLayer.fillColor = UIColor(red:0.99, green:0.13, blue:0.25, alpha:1.0).cgColor
            tick?.layer.addSublayer(arrowLayer)         
        } else {
            print(scrollView, "timeline collection view")
        }


    }

这在子视图顶部绘制了一个漂亮的箭头。

谢谢,我会试试看。为什么在我添加这4条线时它会消失?我已经了解到,当我设置arrowView时它仍然会出现。clipsToBounds=false,所以是我设置原点和/或大小弄乱了它?当你定义UIView时,它需要原点和size.当您注释掉这些行时,它会消失,因为它不知道要放在哪里。--摘自文档:您也可以通过编程方式创建视图。创建视图时,通常指定其相对于未来超级视图的初始大小和位置。--签出“创建视图”靠近顶部的部分:尝试使箭头视图的高度等于其父视图,而不是100