Tips, About: Automatize ActiveLabel links generation

This commit is contained in:
Antonio 2020-03-12 13:13:02 +01:00
parent 3feac2a2e5
commit c02a36c976
11 changed files with 116 additions and 148 deletions

View file

@ -66,6 +66,8 @@
A93159D8241A36B000218092 /* AdafruitBoard+Assets.swift in Sources */ = {isa = PBXBuildFile; fileRef = A93159D6241A36B000218092 /* AdafruitBoard+Assets.swift */; };
A93159DA241A37A900218092 /* QuaternionUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = A93159D9241A37A900218092 /* QuaternionUtils.swift */; };
A93159DB241A37A900218092 /* QuaternionUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = A93159D9241A37A900218092 /* QuaternionUtils.swift */; };
A93159DD241A5B6000218092 /* ActiveLabelUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = A93159DC241A5B6000218092 /* ActiveLabelUtils.swift */; };
A93159DE241A5B6000218092 /* ActiveLabelUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = A93159DC241A5B6000218092 /* ActiveLabelUtils.swift */; };
A948298D237B73B100B6FC13 /* HelpViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9482989237B73B100B6FC13 /* HelpViewController.swift */; };
A948298E237B73B100B6FC13 /* CommonTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A948298A237B73B100B6FC13 /* CommonTableViewCell.swift */; };
A948298F237B73B100B6FC13 /* ModulePanelViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A948298B237B73B100B6FC13 /* ModulePanelViewController.swift */; };
@ -324,6 +326,7 @@
A93159D1241A335700218092 /* QuaternionPanelViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QuaternionPanelViewController.swift; sourceTree = "<group>"; };
A93159D6241A36B000218092 /* AdafruitBoard+Assets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AdafruitBoard+Assets.swift"; sourceTree = "<group>"; };
A93159D9241A37A900218092 /* QuaternionUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QuaternionUtils.swift; sourceTree = "<group>"; };
A93159DC241A5B6000218092 /* ActiveLabelUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActiveLabelUtils.swift; sourceTree = "<group>"; };
A9482989237B73B100B6FC13 /* HelpViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HelpViewController.swift; sourceTree = "<group>"; };
A948298A237B73B100B6FC13 /* CommonTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CommonTableViewCell.swift; sourceTree = "<group>"; };
A948298B237B73B100B6FC13 /* ModulePanelViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ModulePanelViewController.swift; sourceTree = "<group>"; };
@ -770,6 +773,7 @@
A9D5B21223E25C1600178E1B /* ActiveLabelIntrinsicSizeFix.swift */,
A9D12EAA23E902BB00F3C259 /* Float+Clamped.swift */,
A9D12EA423E8F91300F3C259 /* LowPassFilterSignal.swift */,
A93159DC241A5B6000218092 /* ActiveLabelUtils.swift */,
);
path = Utils;
sourceTree = "<group>";
@ -1192,6 +1196,7 @@
A9ADE5F923533D5400777535 /* NeopixelsColorPaletteViewController.swift in Sources */,
A9EAA19523514B2800FA615E /* CornerShadowButton.swift in Sources */,
A96AA5D523635A89004A06BA /* NSLayoutConstraint+ChangeMultiplier.swift in Sources */,
A93159DD241A5B6000218092 /* ActiveLabelUtils.swift in Sources */,
A96AA5C823633EB3004A06BA /* UIButton+BackgroundColorForState.swift in Sources */,
A92256912411CEEA00B8AB57 /* BlePeripheral+AdafruitToneGenerator.swift in Sources */,
A9A08B2523A2CBCF00498069 /* LightSequenceAnimation.swift in Sources */,
@ -1367,6 +1372,7 @@
A9A08B6D23A523C900498069 /* NormalBrightnessColorPickerController.swift in Sources */,
A9A08B6E23A523C900498069 /* PanelInsetView.swift in Sources */,
A9A08B6F23A523C900498069 /* LogHelper.swift in Sources */,
A93159DE241A5B6000218092 /* ActiveLabelUtils.swift in Sources */,
A9A08BB123A7966300498069 /* BlePeripheralSimulated+AdafruitTemperature.swift in Sources */,
A9A08B7023A523C900498069 /* CommonTableViewCell.swift in Sources */,
A9A08BA123A5260300498069 /* BleManagerSimulated.swift in Sources */,

View file

@ -151,6 +151,5 @@ extension BlePeripheral {
}
return amplitudePerChannel
}
}

View file

@ -1236,16 +1236,16 @@
<rect key="frame" x="20" y="0.0" width="374" height="510"/>
<subviews>
<stackView opaque="NO" contentMode="scaleToFill" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="PZf-yf-AWg">
<rect key="frame" x="12" y="-25" width="350" height="560"/>
<rect key="frame" x="40" y="-25" width="334" height="560"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="ljR-bi-cvW">
<rect key="frame" x="0.0" y="0.0" width="212" height="560"/>
<rect key="frame" x="0.0" y="0.0" width="196" height="560"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="1000" verticalHuggingPriority="1000" image="sound_level_dimmed" translatesAutoresizingMaskIntoConstraints="NO" id="LlW-Yf-hDl">
<rect key="frame" x="0.0" y="0.0" width="212" height="560"/>
<rect key="frame" x="0.0" y="0.0" width="196" height="560"/>
</imageView>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="249" verticalHuggingPriority="249" horizontalCompressionResistancePriority="249" verticalCompressionResistancePriority="249" image="sound_level" translatesAutoresizingMaskIntoConstraints="NO" id="xqN-Wx-HD5">
<rect key="frame" x="0.0" y="0.0" width="212" height="560"/>
<rect key="frame" x="0.0" y="0.0" width="196" height="560"/>
</imageView>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
@ -1261,19 +1261,19 @@
</constraints>
</view>
<view contentMode="scaleToFill" horizontalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="yug-x0-ADE">
<rect key="frame" x="220" y="0.0" width="130" height="560"/>
<rect key="frame" x="204" y="0.0" width="130" height="560"/>
<subviews>
<stackView opaque="NO" contentMode="scaleToFill" alignment="bottom" translatesAutoresizingMaskIntoConstraints="NO" id="qoc-mM-xcg">
<rect key="frame" x="12" y="0.0" width="118" height="560"/>
<rect key="frame" x="20.5" y="0.0" width="109.5" height="560"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="750" verticalHuggingPriority="750" verticalCompressionResistancePriority="1000" text="990" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" translatesAutoresizingMaskIntoConstraints="NO" id="Knc-sL-2af">
<rect key="frame" x="0.0" y="509.5" width="81" height="50.5"/>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="750" verticalHuggingPriority="750" verticalCompressionResistancePriority="1000" text="120" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" translatesAutoresizingMaskIntoConstraints="NO" id="Knc-sL-2af">
<rect key="frame" x="0.0" y="509.5" width="72.5" height="50.5"/>
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="42"/>
<color key="textColor" red="1" green="0.99998790029999995" blue="0.9999920726" alpha="1" colorSpace="custom" customColorSpace="displayP3"/>
<nil key="highlightedColor"/>
</label>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="KJ5-tm-SpG">
<rect key="frame" x="81" y="510" width="37" height="50"/>
<rect key="frame" x="72.5" y="510" width="37" height="50"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="1000" verticalHuggingPriority="1000" verticalCompressionResistancePriority="1000" text="dB" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" translatesAutoresizingMaskIntoConstraints="NO" id="Q4H-RL-ETg">
<rect key="frame" x="0.0" y="10" width="37" height="36"/>
@ -1308,7 +1308,7 @@
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="PZf-yf-AWg" secondAttribute="trailing" id="GGu-YO-PfI"/>
<constraint firstItem="PZf-yf-AWg" firstAttribute="centerX" secondItem="uDZ-NY-sb6" secondAttribute="centerX" id="MGz-vS-nUz"/>
<constraint firstItem="PZf-yf-AWg" firstAttribute="centerX" secondItem="uDZ-NY-sb6" secondAttribute="centerX" constant="20" id="MGz-vS-nUz"/>
<constraint firstItem="PZf-yf-AWg" firstAttribute="centerY" secondItem="uDZ-NY-sb6" secondAttribute="centerY" id="nq2-ge-7Ff"/>
<constraint firstItem="PZf-yf-AWg" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="uDZ-NY-sb6" secondAttribute="leading" id="qQS-GM-BbV"/>
</constraints>

View file

@ -10,7 +10,7 @@ import Foundation
struct SensorDataSeries<T>: Sequence, IteratorProtocol {
// Config
let kMaxNumItems = 1000
let kMaxNumItems = 10//1000
// Data
struct Entry {
@ -18,7 +18,7 @@ struct SensorDataSeries<T>: Sequence, IteratorProtocol {
var timestamp: CFAbsoluteTime
}
private var insertIndex = -1
private var lastInsertIndex = -1 // Last index where a value was inserted
private var values = [Entry]()
private var valuesLock = NSLock()
@ -29,13 +29,14 @@ struct SensorDataSeries<T>: Sequence, IteratorProtocol {
// Acccesors
mutating func addValue(_ value: Entry) {
valuesLock.lock(); defer { valuesLock.unlock() }
insertIndex = (insertIndex + 1) % kMaxNumItems
if insertIndex == values.count {
let insertIndex = (lastInsertIndex + 1) % kMaxNumItems
if insertIndex == values.count { // Array not full. Add value
values.insert(value, at: insertIndex)
}
else {
else { // Array full, replace value
values[insertIndex] = value
}
lastInsertIndex = insertIndex
}
subscript(index: Int) -> Entry {
@ -56,7 +57,7 @@ struct SensorDataSeries<T>: Sequence, IteratorProtocol {
// MARK: - Utils
private func internalIndex(_ index: Int) -> Int {
let startIndex = insertIndex - (values.count - 1)
let startIndex = lastInsertIndex - (values.count - 1)
return mod((startIndex + index), kMaxNumItems) // Use mod instead of %, because numerator could be negative
}

View file

@ -15,7 +15,7 @@
// Welcome
"tip0_title" = "Welcome";
"tip0_detail" = "Bluefruit Playground provides you with a variety of tools to communicate with Circuit Playground Bluefruit & Adafruit CLUE!
"tip0_text" = "Bluefruit Playground provides you with a variety of tools to communicate with Circuit Playground Bluefruit & Adafruit CLUE!
Click the links above to purchase a board from the Adafruit shop.";
"tip0_link0_text" = "Circuit Playground Bluefruit";
"tip0_link0_url" = "https://www.adafruit.com/product/4333";
@ -23,12 +23,12 @@ Click the links above to purchase a board from the Adafruit shop.";
"tip0_link1_url" = "https://www.adafruit.com/product/4500";
"tip0_action" = "Let's get started...";
"tip1_title" = "Power Up";
"tip1_detail" = "First, follow the instructions here to load the correct firmware. Then, connect your Bluefruit device to USB, AAA battery pack or a Lipoly battery to power up.";
"tip1_text" = "First, follow the instructions here to load the correct firmware. Then, connect your Bluefruit device to USB, AAA battery pack or a Lipoly battery to power up.";
"tip1_link_text" = "instructions here";
"tip1_link_url" = "https://learn.adafruit.com/bluefruit-playground-app/firmware";
"tip1_action" = "Next";
"tip2_title" = "Discover";
"tip2_detail" = "Once your Bluefruit device is powered, move it close to your iPhone.";
"tip2_text" = "Once your Bluefruit device is powered, move it close to your iPhone.";
"tip2_action" = "Begin pairing";

View file

@ -0,0 +1,64 @@
//
// ActiveLabelUtils.swift
// BluefruitPlayground
//
// Created by Antonio García on 12/03/2020.
// Copyright © 2020 Adafruit. All rights reserved.
//
import UIKit
import ActiveLabel
struct ActiveLabelUtils {
public static func addActiveLabelLinks(label: ActiveLabel, linksLocalizationStringsIdPrefix: String) {
let localizationManager = LocalizationManager.shared
label.text = localizationManager.localizedString("\(linksLocalizationStringsIdPrefix)_text")
label.customize { label in
//let kMaxLinksNumber = 10 // Maximum number of links
let color = UIColor(named: "text_link") ?? .blue
let selectedColor = color.lighter()
label.enabledTypes = [.url]
label.URLColor = color
label.URLSelectedColor = selectedColor
// Search links
var found = true
var i = 0
repeat {
if let linkString = localizationManager.localizedStringIfExists("\(linksLocalizationStringsIdPrefix)_link\(i)_text") {
let customType = ActiveType.custom(pattern: "(\\w*\(linkString)\\w*)")
label.enabledTypes.append(customType)
label.customColor[customType] = color
label.customSelectedColor[customType] = selectedColor
if let linkUrl = URL(string: localizationManager.localizedString("\(linksLocalizationStringsIdPrefix)_link\(i)_url")) {
label.handleCustomTap(for: customType) { _ in
UIApplication.shared.open(linkUrl, options: [:], completionHandler: nil)
}
}
i = i + 1
}
else {
found = false
}
} while(found/* && i < kMaxLinksNumber*/)
label.handleURLTap { url in
UIApplication.shared.open(url, options: [:], completionHandler: nil)
}
label.configureLinkAttribute = { (type, attributes, isSelected) in
var atts = attributes
if label.enabledTypes.contains(type) {
atts[.underlineStyle] = NSUnderlineStyle.single.rawValue
}
return atts
}
}
}
}

View file

@ -58,13 +58,7 @@ class LocalizationManager {
}
func localizedString(_ key: String, description: String?) -> String {
var result: String!
if let string = localizationBundle?.localizedString(forKey: key, value: description, table: nil) {
result = string
} else {
result = key
}
var result = localizationBundle?.localizedString(forKey: key, value: description, table: nil) ?? key
if LocalizationManager.kDebugShowDummyCharacters {
result = String(repeating: "x", count: result.count)
@ -72,4 +66,9 @@ class LocalizationManager {
return result
}
func localizedStringIfExists(_ key: String) -> String? {
let result = localizationBundle?.localizedString(forKey: key, value: nil, table: nil)
return result == key ? nil : result
}
}

View file

@ -16,15 +16,19 @@ class LightChartPanelViewController: ChartPanelViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Init data
// Localization
let localizationManager = LocalizationManager.shared
titleLabel.text = localizationManager.localizedString("lightsensor_chartpanel_title")
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// Reload data
if let board = AdafruitBoardsManager.shared.currentBoard {
// Load initial data
reloadChartEntries(dataSeries: board.lightDataSeries)
}
// Localization
let localizationManager = LocalizationManager.shared
titleLabel.text = localizationManager.localizedString("lightsensor_chartpanel_title")
}
// MARK: - Actions

View file

@ -33,71 +33,9 @@ class AboutViewController: UIViewController {
self.title = localizationManager.localizedString("about_title")
appNameLabel.text = localizationManager.localizedString("about_app_name")
messageLabel.text = localizationManager.localizedString("about_ios_text")
doneButton.title = localizationManager.localizedString("dialog_done")
messageLabel.customize { label in
//let strongString = localizationManager.localizedString("about_ios_strong_text")
let linkString0 = localizationManager.localizedString("about_ios_link0_text")
let linkString1 = localizationManager.localizedString("about_ios_link1_text")
let linkString2 = localizationManager.localizedString("about_ios_link2_text")
let linkString3 = localizationManager.localizedString("about_ios_link3_text")
let customType0 = ActiveType.custom(pattern: "(\\w*\(linkString0)\\w*)")
let customType1 = ActiveType.custom(pattern: "(\\w*\(linkString1)\\w*)")
let customType2 = ActiveType.custom(pattern: "(\\w*\(linkString2)\\w*)")
let customType3 = ActiveType.custom(pattern: "(\\w*\(linkString3)\\w*)")
label.enabledTypes = [customType0, customType1, customType2, customType3]
let color = UIColor(named: "text_link")
let selectedColor = color?.lighter()
label.customColor[customType0] = color
label.customSelectedColor[customType0] = selectedColor
label.customColor[customType1] = color
label.customSelectedColor[customType1] = selectedColor
label.customColor[customType2] = color
label.customSelectedColor[customType2] = selectedColor
label.customColor[customType3] = color
label.customSelectedColor[customType3] = selectedColor
label.handleCustomTap(for: customType0) { _ in
if let url = URL(string: localizationManager.localizedString("about_ios_link0_url")) {
UIApplication.shared.open(url, options: [:], completionHandler: nil)
}
}
label.handleCustomTap(for: customType1) { _ in
if let url = URL(string: localizationManager.localizedString("about_ios_link1_url")) {
UIApplication.shared.open(url, options: [:], completionHandler: nil)
}
}
label.handleCustomTap(for: customType2) { _ in
if let url = URL(string: localizationManager.localizedString("about_ios_link2_url")) {
UIApplication.shared.open(url, options: [:], completionHandler: nil)
}
}
label.handleCustomTap(for: customType3) { _ in
if let url = URL(string: localizationManager.localizedString("about_ios_link3_url")) {
UIApplication.shared.open(url, options: [:], completionHandler: nil)
}
}
label.configureLinkAttribute = { (type, attributes, isSelected) in
var atts = attributes
switch type {
case customType0, customType1:
atts[.underlineStyle] = NSUnderlineStyle.single.rawValue
default: ()
}
return atts
}
}
ActiveLabelUtils.addActiveLabelLinks(label: messageLabel, linksLocalizationStringsIdPrefix: "about_ios")
}
override func viewWillAppear(_ animated: Bool) {

View file

@ -26,24 +26,17 @@ class TipViewController: UIViewController {
titleLabel.text = titleText
}
}
var detailText: String? {
var detailTextLocalizationStringPrefix: String? {
didSet {
loadViewIfNeeded()
detailLabel.text = detailText
}
}
var detailTextLinkString: String? {
didSet {
loadViewIfNeeded()
updateLink()
}
}
var detailTextLinkUrl: URL? {
didSet {
loadViewIfNeeded()
updateLink()
if let detailTextLocalizationStringPrefix = detailTextLocalizationStringPrefix {
ActiveLabelUtils.addActiveLabelLinks(label: detailLabel, linksLocalizationStringsIdPrefix: detailTextLocalizationStringPrefix)
}
else {
DLog("Warning: detailTextLocalizationStringPrefix is nil")
}
}
}
@ -62,39 +55,9 @@ class TipViewController: UIViewController {
containerView.layer.cornerRadius = 8
containerView.layer.masksToBounds = true
//detailLabel.enabledTypes = [.url]
updateLink()
}
@IBAction func action(_ sender: Any) {
actionHandler?()
}
private func updateLink() {
detailLabel.customize { label in
guard let linkString = detailTextLinkString else { return }
let customType = ActiveType.custom(pattern: "(\\w*\(linkString)\\w*)")
label.enabledTypes = [customType]
label.customColor[customType] = UIColor(named: "text_link")
label.customSelectedColor[customType] = UIColor(named: "text_link")?.lighter()
label.handleCustomTap(for: customType) { [unowned self] _ in
if let url = self.detailTextLinkUrl {
UIApplication.shared.open(url, options: [:], completionHandler: nil)
}
}
label.configureLinkAttribute = { (type, attributes, isSelected) in
var atts = attributes
switch type {
case customType:
atts[.underlineStyle] = NSUnderlineStyle.single.rawValue
default: ()
}
return atts
}
}
}
}

View file

@ -48,13 +48,7 @@ class TipsViewController: UIViewController {
if let tipViewController = addTipViewController() {
let titleStringId = String(format: "tip%ld_title", i)
tipViewController.titleText = localizationManager.localizedString(titleStringId)
let detailStringId = String(format: "tip%ld_detail", i)
tipViewController.detailText = localizationManager.localizedString(detailStringId)
let detailTextLinkStringId = String(format: "tip%ld_link_text", i)
tipViewController.detailTextLinkString = localizationManager.localizedString(detailTextLinkStringId)
let detailTextLinkUrlId = String(format: "tip%ld_link_url", i)
tipViewController.detailTextLinkUrl = URL(string: localizationManager.localizedString(detailTextLinkUrlId))
tipViewController.detailTextLocalizationStringPrefix = localizationManager.localizedString(String(format: "tip%ld", i))
let actionStringId = String(format: "tip%ld_action", i)
tipViewController.actionText = localizationManager.localizedString(actionStringId).uppercased()