如何在 Swift 中实现范围滑块

2024-02-28

我正在尝试实现范围滑块,并且使用了名为的自定义控件NMR范围滑块 https://www.cocoacontrols.com/controls/nmrangeslider.

但是当我使用它时,滑块根本不出现。难道也是因为它都是用 Objective-C 写的?

这就是我目前的实现方式:

var rangeSlider = NMRangeSlider(frame: CGRectMake(16, 6, 275, 34))
rangeSlider.lowerValue = 0.54
rangeSlider.upperValue = 0.94
self.view.addSubview(rangeSlider)

要创建自定义范围滑块,我在这里找到了一个很好的解决方案:iOS 8 测距仪教程 https://www.raywenderlich.com/76433/how-to-make-a-custom-control-swift但我的项目在 swift 3 中需要这个。我在这里为 Swift 3 iOS 10 更新了这个:

  1. 在主视图控制器中将其添加到 viewDidLayOut 以显示范围滑块。

    override func viewDidLayoutSubviews() {
       let margin: CGFloat = 20.0
       let width = view.bounds.width - 2.0 * margin
       rangeSlider.frame = CGRect(x: margin, y: margin + topLayoutGuide.length + 170, width: width, height: 31.0)
    }
    
  2. 创建辅助函数以在 viewDidLayoutSubviews() 下面打印滑块输出

    func rangeSliderValueChanged() { //rangeSlider: RangeSlider
        print("Range slider value changed: \(rangeSlider.lowerValue) \(rangeSlider.upperValue) ")//(\(rangeSlider.lowerValue) \(rangeSlider.upperValue))
    }
    
  3. 创建文件RangeSlider.swift并将其添加到其中:

    import UIKit
    
    import QuartzCore
    
    class RangeSlider: UIControl {
    
     var minimumValue = 0.0
     var maximumValue = 1.0
     var lowerValue = 0.2
     var upperValue = 0.8
    
     let trackLayer = RangeSliderTrackLayer()//= CALayer() defined in RangeSliderTrackLayer.swift
     let lowerThumbLayer = RangeSliderThumbLayer()//CALayer()
     let upperThumbLayer = RangeSliderThumbLayer()//CALayer()
     var previousLocation = CGPoint()
    
     var trackTintColor = UIColor(white: 0.9, alpha: 1.0)
     var trackHighlightTintColor = UIColor(red: 0.0, green: 0.45, blue: 0.94, alpha: 1.0)
     var thumbTintColor = UIColor.white
    
     var curvaceousness : CGFloat = 1.0
    
     var thumbWidth: CGFloat {
       return CGFloat(bounds.height)
     }
    
     override init(frame: CGRect) {
       super.init(frame: frame)
    
       trackLayer.rangeSlider = self
       trackLayer.contentsScale = UIScreen.main.scale
       layer.addSublayer(trackLayer)
    
       lowerThumbLayer.rangeSlider = self
       lowerThumbLayer.contentsScale = UIScreen.main.scale
       layer.addSublayer(lowerThumbLayer)
    
       upperThumbLayer.rangeSlider = self
       upperThumbLayer.contentsScale = UIScreen.main.scale
       layer.addSublayer(upperThumbLayer)
    
      }
    
     required init?(coder: NSCoder) {
       super.init(coder: coder)
     }
    
     func updateLayerFrames() {
       trackLayer.frame = bounds.insetBy(dx: 0.0, dy: bounds.height / 3)
       trackLayer.setNeedsDisplay()
    
       let lowerThumbCenter = CGFloat(positionForValue(value: lowerValue))
    
       lowerThumbLayer.frame = CGRect(x: lowerThumbCenter - thumbWidth / 2.0, y: 0.0,
                                   width: thumbWidth, height: thumbWidth)
       lowerThumbLayer.setNeedsDisplay()
    
       let upperThumbCenter = CGFloat(positionForValue(value: upperValue))
       upperThumbLayer.frame = CGRect(x: upperThumbCenter - thumbWidth / 2.0, y: 0.0,
                                   width: thumbWidth, height: thumbWidth)
       upperThumbLayer.setNeedsDisplay()
     }
    
     func positionForValue(value: Double) -> Double {
      return Double(bounds.width - thumbWidth) * (value - minimumValue) /
        (maximumValue - minimumValue) + Double(thumbWidth / 2.0)
     }
    
      override var frame: CGRect {
       didSet {
          updateLayerFrames()
        }
      }
    
       override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
          previousLocation = touch.location(in: self)
    
          // Hit test the thumb layers
          if lowerThumbLayer.frame.contains(previousLocation) {
            lowerThumbLayer.highlighted = true
          } else if upperThumbLayer.frame.contains(previousLocation) {
            upperThumbLayer.highlighted = true
          }
    
           return lowerThumbLayer.highlighted || upperThumbLayer.highlighted
       }
    
       func boundValue(value: Double, toLowerValue lowerValue: Double, upperValue: Double) -> Double {
         return min(max(value, lowerValue), upperValue)
       }
    
       override func continueTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
         let location = touch.location(in: self)
    
         // 1. Determine by how much the user has dragged
         let deltaLocation = Double(location.x - previousLocation.x)
         let deltaValue = (maximumValue - minimumValue) * deltaLocation / Double(bounds.width - thumbWidth)
    
          previousLocation = location
    
          // 2. Update the values
         if lowerThumbLayer.highlighted {
            lowerValue += deltaValue
            lowerValue = boundValue(value: lowerValue, toLowerValue: minimumValue, upperValue: upperValue)
        } else if upperThumbLayer.highlighted {
          upperValue += deltaValue
          upperValue = boundValue(value: upperValue, toLowerValue: lowerValue, upperValue: maximumValue)
        }
    
        // 3. Update the UI
        CATransaction.begin()
        CATransaction.setDisableActions(true)
    
        updateLayerFrames()
    
        CATransaction.commit()
    
         sendActions(for: .valueChanged)
    
          return true
     }
    
      override func endTracking(_ touch: UITouch?, with event: UIEvent?) {
        lowerThumbLayer.highlighted = false
        upperThumbLayer.highlighted = false
      }
    }
    
  4. 接下来添加thumb层子类文件RangeSliderThumbLayer.swift并将其添加到其中:

      import UIKit
    
      class RangeSliderThumbLayer: CALayer {
         var highlighted = false
         weak var rangeSlider: RangeSlider?
    
      override func draw(in ctx: CGContext) {
        if let slider = rangeSlider {
          let thumbFrame = bounds.insetBy(dx: 2.0, dy: 2.0)
          let cornerRadius = thumbFrame.height * slider.curvaceousness / 2.0
          let thumbPath = UIBezierPath(roundedRect: thumbFrame, cornerRadius: cornerRadius)
    
          // Fill - with a subtle shadow
          let shadowColor = UIColor.gray
          ctx.setShadow(offset: CGSize(width: 0.0, height: 1.0), blur: 1.0, color: shadowColor.cgColor)
          ctx.setFillColor(slider.thumbTintColor.cgColor)
          ctx.addPath(thumbPath.cgPath)
          ctx.fillPath()
    
        // Outline
        ctx.setStrokeColor(shadowColor.cgColor)
        ctx.setLineWidth(0.5)
        ctx.addPath(thumbPath.cgPath)
        ctx.strokePath()
    
        if highlighted {
            ctx.setFillColor(UIColor(white: 0.0, alpha: 0.1).cgColor)
            ctx.addPath(thumbPath.cgPath)
            ctx.fillPath()
        }
      }
     }
    }
    
  5. 最后添加轨迹层子类文件RangeSliderTrackLayer.swift并向其中添加以下内容:

     import Foundation
     import UIKit
     import QuartzCore
    
     class RangeSliderTrackLayer: CALayer {
      weak var rangeSlider: RangeSlider?
    
      override func draw(in ctx: CGContext) {
        if let slider = rangeSlider {
          // Clip
          let cornerRadius = bounds.height * slider.curvaceousness / 2.0
          let path = UIBezierPath(roundedRect: bounds, cornerRadius: cornerRadius)
          ctx.addPath(path.cgPath)
    
          // Fill the track
          ctx.setFillColor(slider.trackTintColor.cgColor)
          ctx.addPath(path.cgPath)
          ctx.fillPath()
    
          // Fill the highlighted range
          ctx.setFillColor(slider.trackHighlightTintColor.cgColor)
          let lowerValuePosition =  CGFloat(slider.positionForValue(value: slider.lowerValue))
          let upperValuePosition = CGFloat(slider.positionForValue(value: slider.upperValue))
          let rect = CGRect(x: lowerValuePosition, y: 0.0, width: upperValuePosition - lowerValuePosition, height: bounds.height)
        ctx.fill(rect)
       }
      }
     }
    

Build Run and Get: enter image description here

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在 Swift 中实现范围滑块 的相关文章

  • 如何使 collectionViewCell 仅按高度自动调整大小?

    我使用collectionView的自动调整流布局的功能 self flow UICollectionViewFlowLayout alloc init self flow scrollDirection UICollectionViewS
  • 当 UIView 通过自动布局调整大小时,会调用什么方法?

    我有一个图像视图 我通过在子类中覆盖以下内容来实现圆角 void setFrame CGRect frame super setFrame frame self layout setCornerRadius frame size width
  • 如何在 macOS 上的 SwiftUI 中设置拖动图像

    我正在尝试更改 GridView 的默认拖放预览图像 该图像似乎包含网格中的所有可见项目 据我了解 我应该能够设置NSItemProvider previewImageHandler块来自定义所使用的图像 我似乎找不到任何关于返回自定义图像
  • SwiftUI 转义闭包捕获变异的“self”参数

    我有一个可以通过两种方式打开的视图 一个包含提供给它的数据 另一个包含对 Firestore 文档的文档引用 我创建了两个构造函数 在第一个构造函数中我提供数据 在另一个构造函数中我提供文档参考 然后我使用此引用进行网络调用 但出现错误 E
  • 如何在UIWindow中添加视图?

    我想添加一个视图UIWindow与以下代码 AppDelegate delegate AppDelegate UIApplication sharedApplication delegate UIWindow window delegate
  • 在 Objective-C 中比较两次的最好/最简单的方法是什么?

    我有一个时间的字符串表示形式 例如 11 13 AM 这是使用 NSDateFormatter 和 stringFromDate 方法生成的 我想将此时间与当前时间进行比较 但是当我使用 dateFromString 方法将字符串转回日期时
  • 如何在 UINavigationController 中收到弹出视图的通知?

    我想在用户按下我的后退按钮时执行操作UINavigationController当到达某个时UIViewController 不幸的是它看起来像UINavigationControllerDelegate没有任何方法来获取视图弹出的通知 作
  • Objective C - 动态属性的respondsToSelector

    我目前面临的问题是检查对象 NSManagedObject 的属性是否存在 不幸的是方法 MyObject class respondsToSelector selector myProperty 总是返回NO 我认为这是因为CoreDat
  • iOS - 检测应用程序是否正在从 Xcode 运行 [重复]

    这个问题在这里已经有答案了 我试图根据代码是否通过 USB Xcode 调试 运行或在从应用程序商店下载的生产模式 发布 运行来启用 禁用部分代码 我知道检查它是否正在运行DEBUG or RELEASE像这样的模式 ifdef DEBUG
  • Swift 每 5 天重复一次 LocalNotification

    如何每 5 天上午 10 00 重复一次 LocalNotification 我尝试这个 但它不起作用 let content UNMutableNotificationContent content title Hello content
  • Xcode 8.2 更新后二进制文件无效

    我今天尝试在更新到 Xcode 8 2 后向我的应用程序推送更新 但收到无效的二进制错误 我以前从未见过这个 我的应用程序的 iOS 部署目标是 iOS 9 0 有谁见过这个错误或知道如何修复它 这是电子邮件的内容 解释了二进制文件的无效内
  • iOS 应用程序崩溃 com.apple.root.background-qos

    在应用程序中发现应用程序崩溃 我怀疑这可能是由于 firebase 观察者的代码而发生的 由于在用户案例中 用户可以从一个事件转到用户配置文件 参与此事件 然后从用户配置文件可以返回到此事件 我需要一个 ref 句柄来删除特定的观察者 因此
  • 使用 popToRootViewController 时我丢失了导航栏

    我有一个 iOS 应用程序 其中主屏幕是 UICollectionViewController 从集合视图中选择项目时 视图将推送到该项目的详细信息视图 在细节视图中 我构建了一个从侧面移出的抽屉 滑块 为了让视图看起来像我想要的那样 我隐
  • 如何在 Swift 中将 Int 转换为字符

    我在这里挣扎了十多分钟 失败了 我屈服了 我需要在 Swift 中将 Int 转换为 Character 但无法解决它 Question 你如何转换 cast an Int integer to a Character char 在斯威夫特
  • UITextField 中光标闪烁,但键盘不出现

    我得到了一个带有文本字段的简单详细视图 在详细的viewController中我写了这段代码 void viewDidAppear BOOL animated self textField becomeFirstResponder NSLo
  • 在 ios 中从 XMPP 服务器检索存档的消息

    我正在将 XMPP 功能集成到我的 ios 应用程序中 但遇到了一个无法解决的问题 问题是我无法从服务器获取存档的消息 我的客户能够登录 并且我已经成功测试了多个服务调用 发送 接收消息 获取有关用户的信息 发送后
  • 如何使用CAAnimation制作曲线/圆弧动画?

    我有一个用户界面 其中一个项目被删除 我想模仿 iOS 邮件中的 移动到文件夹 效果 小字母图标被 扔 到文件夹中的效果 我的会被扔进垃圾箱 我尝试使用它来实现它CAAnimation在图层上 据我在文档中阅读 我应该能够设置byValue
  • 从 Xcode iOS 项目运行 swift 脚本作为构建阶段

    这是一个简单的快速脚本 usr bin env xcrun swift import Foundation let task NSTask task launchPath bin echo task arguments farg1 arg2
  • Google Maps iOS SDK:地图上东西点之间的距离

    如何计算地图东点和西点之间的距离 以米为单位 假设用户更改了滚动地图的位置 然后我使用 mapView didChangeCameraPosition delegate 方法捕获移动 但我不知道如何计算距离 这是一个辅助函数 可用于计算两个
  • Swift 3 中的 NSFetchedResultsController 删除缓存

    目前正在迁移到 swift 3 无法完全弄清楚解析器想要什么NSFetchedResultsController deleteCache withName rootCache 使用这种语法 我得到一个 Type String 构建时出现不符

随机推荐

  • 具有动态参数的表达式树

    我想转换这个 Func
  • 在 try catch 中访问变量

    我在返回 menuFont 行上不断收到编译错误 它表示没有变量 menuFont 有人可以告诉我如何解决这个问题吗 import java awt Font import java awt FontFormatException impo
  • 发送电子邮件的代码

    我在这里做错了什么 private void SendMail string from string body string mailServerName plus pop mail yahoo com MailMessage messag
  • 删除字符串中的空格

    我必须摆脱超过 1 个空格也就是说 如果有超过 1 个空格 我会将其替换为单个空格 这就是我的做法 但我真的很困惑哪种方法是最好的方法以及所有这些方法有什么区别 下面是我的代码 public class SspaceDemo public
  • 已弃用的 API 和旧版 API 之间的区别?

    我正在研究 Java 中的遗留 APICollection Framework我了解到诸如此类的课程Vector and HashTable已被取代ArrayList and HashMap 然而 它们仍然没有被弃用 并且被视为遗留 本质上
  • WordPress 的 Docker 运行缓慢

    Problem 我在使用 WordPress 和 Docker 时遇到问题 因为我的网站加载时间很慢 7 秒 我不确定为什么会发生这种情况 但我认为这与外部数据库或共享卷有关 Setup 我有一个使用 XDebug 和 Mailhog 在
  • C++ - 如何找到整数的长度

    我试图找到一种方法来查找整数的长度 位数 然后将其放入整数数组中 该作业还要求在不使用 STL 中的类的情况下执行此操作 尽管程序规范确实说我们可以使用 通用 C 库 我会问我的教授是否可以使用 cmath 因为我假设 log10 num
  • salesforce 中的联合身份验证和委派身份验证

    有人知道 salesforce 中的联合身份验证和委托身份验证之间的区别吗 您能解释一下这两种方法中的请求流程吗 主要区别在于联合身份验证上安全断言标记语言 SAML 的使用 委托认证如果您的组织中有移动用户 或者您想要启用委派身份验证 单
  • 如何判断我的应用程序是否从后台恢复?

    我想在应用程序进入后台时锁定它 当它恢复时我想显示我自己的锁定屏幕 锁屏是我的应用程序的一个活动 成功输入密码后 用户可以看到恢复的 Activity 否则他不能 我怎样才能做到这一点 主要问题是 当你开始一个项目时 你必须得到一个特定的行
  • 如何在 Django 中将两个模型字段表示为一个表单字段?

    我似乎无法弄清楚如何在 Django 中正确处理以下情况 我在模型中有一个日期范围 我将其存储为两个单独的字段 date start and date end start date models DateTimeField end date
  • 如何解决 C++ 中友元声明的循环依赖?

    为什么以下代码无法编译以及如何修复它 我得到的错误是 使用未声明的标识符 Foo 虽然Foo在错误发生的地方 在friend声明于Bar foo h ifndef FOO H define FOO H include bar h neede
  • 使用位掩码组合枚举值

    我知道可以在枚举值中使用位掩码 但我不知道如何创建它 我有一个简单的枚举 enum State minimizing 0 maximizing minimized maximized 状态总是State minimized or State
  • 在同一行初始化两个变量

    我很难找到这个概念的权威例子或讨论 如果我的 Ruby 方法中有 2 个数字变量 我需要将它们初始化为零 它们将用作计数器 这可以吗 安全吗 它在我的测试中有效 而不是这个 foo 0 bar 0 你可以这样做 foo bar 0 这似乎是
  • 了解Android联系人的架构

    我正在开发一个 Android 应用程序 它需要知道何时添加 更新 删除联系人 所以我读了几篇文章 据我所知 每当联系人发生更改时 我们都可以通过内容观察者收到通知 但我们无法获取已添加 更新 删除的联系人 因此 我阅读了官方 API 并准
  • 来自 API 的 2 个并发请求数据混淆

    我使用 Nodejs 作为我的应用程序 API 的后端 但我意识到当有 2 个不同的用户不断请求同一个方法时 从 MySQL 请求返 回的数据有时可能会混淆 这是我的代码 router get v1 getList function req
  • 使用 Parcel 为浏览器构建 - 如何不输出 CommonJS 或 ES 模块

    我正在尝试编译一个我编写的库 以便可以将分布式文件放入script标记并在浏览器中运行 我正在尝试用 Parcel 2 来做到这一点 我觉得我已经很接近了 但每次我认为我在那里时都会出现一些新问题 关键是我想要它not捆绑外部依赖项 例如
  • 如果使用遗留库,如何避免 Java 中的未检查转换警告?

    我喜欢java中的泛型功能并且经常使用它 但如果我使用尚不支持泛型的库 我就会遇到问题 Servlet 就是一个例子 如果你使用ServletRequest getParameterMap 结果将是一个原始地图 但它只包括String作为钥
  • 在 R 3.1.1 (Windows) 中安装 rCharts 时出错

    是否有适用于 R 3 1 1 的 rCharts 版本 我尝试了2种方法 均失败 方法一 devtools install github ramnathv rCharts Downloading github repo ramnathv r
  • 是否可以在 Meta 刷新之前运行 JavaScript 代码

    一直以来 我们都在使用这个可靠的网站重定向 HTML JavaScript 代码
  • 如何在 Swift 中实现范围滑块

    我正在尝试实现范围滑块 并且使用了名为的自定义控件NMR范围滑块 https www cocoacontrols com controls nmrangeslider 但是当我使用它时 滑块根本不出现 难道也是因为它都是用 Objectiv