我试图设置取景器显示的彩色标签。我所知道的唯一功能是setResourceValue。但这需要本地化名称!
我可以想象我的母语和英语,但我不知道的其他所有人。我无法相信,这应该是方式。
是翻译功能,它采用像enum或int这样的标准参数并提供本地化的颜色名称吗?
我有一个正在运行的部分,但只有两种语言(德语和英语):
let colorNamesEN = [ "None", "Gray", "Green", "Purple", "Blue", "Yellow", "Red", "Orange" ]
let colorNamesDE = [ "", "Grau", "Grün", "Lila", "Blau", "Gelb", "Rot", "Orange" ]
public enum TagColors : Int8 {
case None = -1, Gray, Green, Purple, Blue, Yellow, Red, Orange, Max
}
//let theURL : NSURL = NSURL.fileURLWithPath("/Users/dirk/Documents/MyLOG.txt")
extension NSURL {
// e.g. theURL.setColors(0b01010101)
func tagColorValue(tagcolor : TagColors) -> UInt16 {
return 1 << UInt16(tagcolor.rawValue)
}
func addTagColor(tagcolor : TagColors) -> Bool {
let bits : UInt16 = tagColorValue(tagcolor) | self.getTagColors()
return setTagColors(bits)
}
func remTagColor(tagcolor : TagColors) -> Bool {
let bits : UInt16 = ~tagColorValue(tagcolor) & self.getTagColors()
return setTagColors(bits)
}
func setColors(tagcolor : TagColors) -> Bool {
let bits : UInt16 = tagColorValue(tagcolor)
return setTagColors(bits)
}
func setTagColors(colorMask : UInt16) -> Bool {
// get string for all available and requested bits
let arr = colorBitsToStrings(colorMask & (tagColorValue(TagColors.Max)-1))
do {
try self.setResourceValue(arr, forKey: NSURLTagNamesKey)
return true
}
catch {
print("Could not write to file \(self.absoluteURL)")
return false
}
}
func getTagColors() -> UInt16 {
return getAllTagColors(self.absoluteURL)
}
}
// let initialBits: UInt8 = 0b00001111
func colorBitsToStrings(colorMask : UInt16) -> NSArray {
// translate bits to (localized!) color names
let countryCode = NSLocale.currentLocale().objectForKey(NSLocaleLanguageCode)!
// I don't know how to automate it for all languages possible!!!!
let colorNames = countryCode as! String == "de" ? colorNamesDE : colorNamesEN
var tagArray = [String]()
var bitNumber : Int = -1 // ignore first loop
for colorName in colorNames {
if bitNumber >= 0 {
if colorMask & UInt16(1<<bitNumber) > 0 {
tagArray.append(colorName)
}
}
bitNumber += 1
}
return tagArray
}
func getAllTagColors(file : NSURL) -> UInt16 {
var colorMask : UInt16 = 0
// translate (localized!) color names to bits
let countryCode = NSLocale.currentLocale().objectForKey(NSLocaleLanguageCode)!
// I don't know how to automate it for all languages possible!!!!
let colorNames = countryCode as! String == "de" ? colorNamesDE : colorNamesEN
var bitNumber : Int = -1 // ignore first loop
var tags : AnyObject?
do {
try file.getResourceValue(&tags, forKey: NSURLTagNamesKey)
if tags != nil {
let tagArray = tags as! [String]
for colorName in colorNames {
if bitNumber >= 0 {
// color name listed?
if tagArray.filter( { $0 == colorName } ).count > 0 {
colorMask |= UInt16(1<<bitNumber)
}
}
bitNumber += 1
}
}
} catch {
// process the error here
}
return colorMask
}
答案 0 :(得分:4)
由于新的URLResourceValues()
结构和标签号,我无需知道颜色名称即可使用。
知道每个标签号代表标签颜色:
0无
1灰色
2绿色
3紫紫
4蓝色
5黄色
6红色
7橙色
制作文件的网址:
var url = URL(fileURLWithPath: pathToYourFile)
它必须是var,因为我们要改变它。
创建一个新的URLResourceValues
实例(也需要是一个变量):
var rv = URLResourceValues()
设置标签编号如下:
rv.labelNumber = 2 // green
最后,将标记写入文件:
do {
try url.setResourceValues(rv)
} catch {
print(error.localizedDescription)
}
在我们的示例中,我们将数字标记设置为2,因此此文件标记为绿色。
答案 1 :(得分:4)
要设置单一颜色,setResourceValue
API调用确实应该使用。但是,您应该使用的资源键是NSURLLabelNumberKey
,或者是Swift 3中的URLResourceKey.labelNumberKey
(不是NSURLTagNamesKey
):
enum LabelNumber: Int {
case none
case grey
case green
case purple
case blue
case yellow
case red
case orange
}
do {
// casting to NSURL here as the equivalent API in the URL value type appears borked:
// setResourceValue(_, forKey:) is not available there,
// and setResourceValues(URLResourceValues) appears broken at least as of Xcode 8.1…
// fix-it for setResourceValues(URLResourceValues) is saying to use [URLResourceKey: AnyObject],
// and the dictionary equivalent also gives an opposite compiler error. Looks like an SDK / compiler bug.
try (fileURL as NSURL).setResourceValue(LabelNumber.purple.rawValue, forKey: .labelNumberKey)
}
catch {
print("Error when setting the label number: \(error)")
}
(这是an answer to a related Objective-C question的Swift 3端口。使用Xcode 8.1测试,macOS Sierra 10.12.1)
要设置多种颜色,您可以使用您在设置资源值时使用的API和标签键。这里描述了这两种编码之间的区别:http://arstechnica.com/apple/2013/10/os-x-10-9/9/ - 基本上标签键是在内部设置扩展属性“com.apple.metadata:_kMDItemUserTags”,它将这些标签字符串的数组存储为二进制plist,而上面显示的单色选项是设置32字节长扩展属性值“com.apple.FinderInfo”的第10个字节。
该密钥名称中的“本地化”有点令人困惑,因为实际设置的是用户选择的标签集,在用户设置的标签名称中。这些标签值确实是本地化的,但仅限于最初创建帐户时根据本地化设置设置的范围。为了演示,这些是Finder在我的系统上使用的标签值,我将其设置为芬兰本地化作为测试并重新启动Finder,重启机器等:
➜ defaults read com.apple.Finder FavoriteTagNames
(
"",
Red,
Orange,
Yellow,
Green,
Blue,
Purple,
Gray
)
数据在二进制plist值中编码的方式只是最喜欢的标记名称,后跟数组中的索引(固定为长度为8,实际值从1开始,即匹配七种颜色订单红色,橙色,黄色,绿色,蓝色,紫色,灰色)。例如:
xattr -p com.apple.metadata:_kMDItemUserTags foobar.png | xxd -r -p | plutil -convert xml1 - -o -
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array>
<string>Gray
1</string>
<string>Purple
3</string>
<string>Green
2</string>
<string>Red
6</string>
</array>
</plist>
因此,系统本地化不会被考虑在内,事实上,使用任何字符串后跟换行设置标签,然后在1-7之间设置一个数字,将在Finder中显示标签索引所指示的颜色。但是,要知道要应用的正确当前值以从最喜欢的标签集中应用标签(这样颜色和标签都匹配),您需要从Finder首选项中读取该键(键入'FavoriteTagNames'来自domain'com.apple.Finder',它编码了一些喜欢的标签名称,如上所示。)
如果您希望标签名称和颜色正确,请忽略上述复杂功能,需要从Finder偏好设置域中读取(您可能会或可能不会这样做,具体取决于您的应用是否如果您希望使用多种颜色,这是一个使用扩展属性值直接设置颜色的示例解决方案(我使用SOExtendedAttributes以避免触及笨重的xattr C API):
enum LabelNumber: Int {
case none
case gray
case green
case purple
case blue
case yellow
case red
case orange
// using an enum here is really for illustrative purposes:
// to know the correct values to apply you would need to read Finder preferences (see body of my response for more detail).
var label:String? {
switch self {
case .none: return nil
case .gray: return "Gray\n1"
case .green: return "Green\n2"
case .purple: return "Purple\n3"
case .blue: return "Blue\n4"
case .yellow: return "Yellow\n5"
case .red: return "Red\n6"
case .orange: return "Orange\n7"
}
}
static func propertyListData(labels: [LabelNumber]) throws -> Data {
let labelStrings = labels.flatMap { $0.label }
let propData = try! PropertyListSerialization.data(fromPropertyList: labelStrings,
format: PropertyListSerialization.PropertyListFormat.binary,
options: 0)
return propData
}
}
do {
try (fileURL as NSURL).setExtendedAttributeData(LabelNumber.propertyListData(labels: [.gray, .green]),
name: "com.apple.metadata:_kMDItemUserTags")
}
catch {
print("Error when setting the label number: \(error)")
}
答案 2 :(得分:2)
<强>记录
首先是我之前的回答,它适用于将一个颜色标签设置为文件:https://stackoverflow.com/a/39751001/2227743。
然后@mz2发布了这个优秀的答案,该答案成功地将几个颜色标签设置到文件中并解释了该过程:https://stackoverflow.com/a/40314367/2227743。
现在这个小插件,一个简单的follow-up到@ mz2的答案。
<强>解决方案强>
我只是简单地实现了@ mz2的建议:我已经使用获取Finder首选项的方法扩展了他的枚举示例,并在设置属性之前提取了正确的本地化标签颜色名称到文件。
> nearby(data = cor, d = 0.3)
[,1] [,2]
[1,] "c" "g"
[2,] "c" "i"
[3,] "d" "e"
[4,] "d" "f"
[5,] "e" "f"
[6,] "g" "i"
[7,] "h" "k"
用法:
enum LabelColors: Int {
case none
case gray
case green
case purple
case blue
case yellow
case red
case orange
func label(using list: [String] = []) -> String? {
if list.isEmpty || list.count < 7 {
switch self {
case .none: return nil
case .gray: return "Gray\n1"
case .green: return "Green\n2"
case .purple: return "Purple\n3"
case .blue: return "Blue\n4"
case .yellow: return "Yellow\n5"
case .red: return "Red\n6"
case .orange: return "Orange\n7"
}
} else {
switch self {
case .none: return nil
case .gray: return list[0]
case .green: return list[1]
case .purple: return list[2]
case .blue: return list[3]
case .yellow: return list[4]
case .red: return list[5]
case .orange: return list[6]
}
}
}
static func set(colors: [LabelColors],
to url: URL,
using list: [String] = []) throws
{
// 'setExtendedAttributeData' is part of https://github.com/billgarrison/SOExtendedAttributes
try (url as NSURL).setExtendedAttributeData(propertyListData(labels: colors, using: list),
name: "com.apple.metadata:_kMDItemUserTags")
}
static func propertyListData(labels: [LabelColors],
using list: [String] = []) throws -> Data
{
let labelStrings = labels.flatMap { $0.label(using: list) }
return try PropertyListSerialization.data(fromPropertyList: labelStrings,
format: .binary,
options: 0)
}
static func localizedLabelNames() -> [String] {
// this doesn't work if the app is Sandboxed:
// the users would have to point to the file themselves with NSOpenPanel
let url = URL(fileURLWithPath: "\(NSHomeDirectory())/Library/SyncedPreferences/com.apple.finder.plist")
let keyPath = "values.FinderTagDict.value.FinderTags"
if let d = try? Data(contentsOf: url) {
if let plist = try? PropertyListSerialization.propertyList(from: d,
options: [],
format: nil),
let pdict = plist as? NSDictionary,
let ftags = pdict.value(forKeyPath: keyPath) as? [[AnyHashable: Any]]
{
var list = [(Int, String)]()
// with '.count == 2' we ignore non-system labels
for item in ftags where item.values.count == 2 {
if let name = item["n"] as? String,
let number = item["l"] as? Int {
list.append((number, name))
}
}
return list.sorted { $0.0 < $1.0 }.map { "\($0.1)\n\($0.0)" }
}
}
return []
}
}
答案 3 :(得分:0)