草庐IT

swift - 如何确保 CAShapeLayer 调整大小以适应 UIView

coder 2023-09-10 原文

我目前正在将 MKPolyline 转换为 BezierPath,然后转换为 CAShapeLayer,然后将该层作为子层添加到 UIView。目前正在努力确保路径不会在 UIView 的边界之外绘制。我不想屏蔽并让部分路径消失,而是确保每个点都调整大小并定位在 UIView 的中心。

func addPathToView() {
    guard let path = createPath(onView: polylineView) else { return }
    path.fit(into: polylineView.bounds).moveCenter(to: polylineView.center).fill()
    path.lineWidth     = 3.0
    path.lineJoinStyle = .round

    guard let layer  = createCAShapeLayer(fromBezierPath: path) else { return }
    layer.path       = getScaledPath(fromPath: path, layer: layer)
    layer.frame      = polylineView.bounds
    layer.position.x = polylineView.bounds.minX
    layer.position.y = polylineView.bounds.minY

    polylineView.layer.addSublayer(layer)
}

func createCAShapeLayer( fromBezierPath path: UIBezierPath? ) -> CAShapeLayer? {
    guard let path = path else { print("No Path"); return nil }
    let pathLayer = CAShapeLayer(path: path, lineColor: UIColor.red, fillColor: UIColor.clear)
    return pathLayer
}

func createPath( onView view: UIView? ) -> UIBezierPath? {
    guard let polyline = Polyline().createPolyline(forLocations: locations) else { print("No Polyline"); return nil }
    guard let points   = convertMapPointsToCGPoints(fromPolyline: polyline) else { print("No CGPoints"); return nil }

    let path = UIBezierPath(points: points)

    return path
}

func convertMapPointsToCGPoints( fromPolyline polyline: MKPolyline? ) -> [CGPoint]? {
    guard let polyline = polyline else { print( "No Polyline"); return nil }

    let mapPoints = polyline.points()

    var points = [CGPoint]()

    for point in 0..<polyline.pointCount {
        let coordinate = MKCoordinateForMapPoint(mapPoints[point])
        points.append(mapView.convert(coordinate, toPointTo: view))
    }

    return points
}

func getScaledPath( fromPath path: UIBezierPath, layer: CAShapeLayer ) -> CGPath? {
    let boundingBox = path.cgPath.boundingBoxOfPath

    let boundingBoxAspectRatio = boundingBox.width / boundingBox.height
    let viewAspectRatio = polylineView.bounds.size.width / polylineView.bounds.size.height

    let scaleFactor: CGFloat
    if (boundingBoxAspectRatio > viewAspectRatio) {
        // Width is limiting factor
        scaleFactor = polylineView.bounds.size.width / boundingBox.width
    } else {
        // Height is limiting factor
        scaleFactor = polylineView.bounds.size.height/boundingBox.height
    }

    var affineTransorm = CGAffineTransform(scaleX: scaleFactor, y: scaleFactor)
    let transformedPath = path.cgPath.copy(using: &affineTransorm)

    guard let tPath = transformedPath else { print ("nope"); return nil }

    return tPath
}

extension UIBezierPath
{
    func moveCenter(to:CGPoint) -> Self{
        let bound  = self.cgPath.boundingBox
        let center = bounds.center

        let zeroedTo = CGPoint(x: to.x-bound.origin.x, y: to.y-bound.origin.y)
        let vector = center.vector(to: zeroedTo)

        offset(to: CGSize(width: vector.dx, height: vector.dy))
        return self
    }

    func offset(to offset:CGSize) -> Self{
        let t = CGAffineTransform(translationX: offset.width, y: offset.height)
        applyCentered(transform: t)
        return self
    }

    func fit(into:CGRect) -> Self{
        let bounds = self.cgPath.boundingBox

        let sw     = into.size.width/bounds.width
        let sh     = into.size.height/bounds.height
        let factor = min(sw, max(sh, 0.0))

        return scale(x: factor, y: factor)
    }

    func scale(x:CGFloat, y:CGFloat) -> Self{
        let scale = CGAffineTransform(scaleX: x, y: y)
        applyCentered(transform: scale)
        return self
    }

    func applyCentered(transform: @autoclosure () -> CGAffineTransform ) -> Self{
        let bound  = self.cgPath.boundingBox
        let center = CGPoint(x: bound.midX, y: bound.midY)
        var xform  = CGAffineTransform.identity

        xform = xform.concatenating(CGAffineTransform(translationX: -center.x, y: -center.y))
        xform = xform.concatenating(transform())
        xform = xform.concatenating( CGAffineTransform(translationX: center.x, y: center.y))
        apply(xform)

        return self
    }
}

extension UIBezierPath
{
    convenience init(points:[CGPoint])
    {
        self.init()

        //connect every points by line.
        //the first point is start point
        for (index,aPoint) in points.enumerated()
        {
            if index == 0 {
                self.move(to: aPoint)
            }
            else {
                self.addLine(to: aPoint)
            }
        }
    }
}

//2. To create layer use this extension

extension CAShapeLayer
{
    convenience init(path:UIBezierPath, lineColor:UIColor, fillColor:UIColor)
    {
        self.init()
        self.path = path.cgPath
        self.strokeColor = lineColor.cgColor
        self.fillColor = fillColor.cgColor
        self.lineWidth = path.lineWidth

        self.opacity = 1
        self.frame = path.bounds
    }
}

最佳答案

UIBezierPath 可以像 CGRectCGPoint 或使用 CGAffineTransform 的“CGSize”一样缩放。 ?

// calculate the scale
//
let scaleWidth  = toSize.width / fromSize.width
let scaleHeight = toSize.height / fromSize.height

// re-scale the path
//
path.apply(CGAffineTransform(scaleX: scaleWidth, y: scaleHeight))

关于swift - 如何确保 CAShapeLayer 调整大小以适应 UIView,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42085795/

有关swift - 如何确保 CAShapeLayer 调整大小以适应 UIView的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  3. ruby-on-rails - 在 Rails 中将文件大小字符串转换为等效千字节 - 2

    我的目标是转换表单输入,例如“100兆字节”或“1GB”,并将其转换为我可以存储在数据库中的文件大小(以千字节为单位)。目前,我有这个:defquota_convert@regex=/([0-9]+)(.*)s/@sizes=%w{kilobytemegabytegigabyte}m=self.quota.match(@regex)if@sizes.include?m[2]eval("self.quota=#{m[1]}.#{m[2]}")endend这有效,但前提是输入是倍数(“gigabytes”,而不是“gigabyte”)并且由于使用了eval看起来疯狂不安全。所以,功能正常,

  4. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  5. ruby-on-rails - 如何验证 update_all 是否实际在 Rails 中更新 - 2

    给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru

  6. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  7. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  8. ruby - 如何指定 Rack 处理程序 - 2

    Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack

  9. ruby - 如何每月在 Heroku 运行一次 Scheduler 插件? - 2

    在选择我想要运行操作的频率时,唯一的选项是“每天”、“每小时”和“每10分钟”。谢谢!我想为我的Rails3.1应用程序运行调度程序。 最佳答案 这不是一个优雅的解决方案,但您可以安排它每天运行,并在实际开始工作之前检查日期是否为当月的第一天。 关于ruby-如何每月在Heroku运行一次Scheduler插件?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/8692687/

  10. ruby-on-rails - 如何从 format.xml 中删除 <hash></hash> - 2

    我有一个对象has_many应呈现为xml的子对象。这不是问题。我的问题是我创建了一个Hash包含此数据,就像解析器需要它一样。但是rails自动将整个文件包含在.........我需要摆脱type="array"和我该如何处理?我没有在文档中找到任何内容。 最佳答案 我遇到了同样的问题;这是我的XML:我在用这个:entries.to_xml将散列数据转换为XML,但这会将条目的数据包装到中所以我修改了:entries.to_xml(root:"Contacts")但这仍然将转换后的XML包装在“联系人”中,将我的XML代码修改为

随机推荐