2016-09-27 2 views
5

파인더에 표시된 컬러 라벨을 설정하려고합니다. 내가 아는 유일한 함수는 setResourceValue입니다. 그러나 이것은 현지화 된 이름이 필요합니다!스위프트 3 : 파인더 라벨 색상을 설정하십시오.

나는 모국어와 영어도 이미지 할 수 있지만 다른 모든 것들은 잘 모릅니다. 나는 이것이 길이어야한다는 것을 믿을 수 없다.

enum이나 int와 같은 표준 매개 변수를 사용하고 지역화 된 색상 이름을 제공하는 번역 기능입니까? 새로운 URLResourceValues() 구조체에 대한 감사와 태그 번호를

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 
} 

답변

2

역사 :), 여러 색상을 사용하고자한다, 여기에 직접 확장 된 속성 값을 사용하여 색상을 설정하는 예 솔루션입니다 (I는 다루기 힘든에서 xattr C API)를 접촉하는 것을 피하기 위해 SOExtendedAttributes을 사용

먼저 내 대답은 색 레이블을 https://stackoverflow.com/a/39751001/2227743으로 설정하는 것입니다.

@mz2 다음은 여러 색상 레이블을 파일에 성공적으로 설정하고 프로세스를 설명하는 우수 답변입니다. https://stackoverflow.com/a/40314367/2227743.

이제이 작은 추가 기능은 @@ mz2의 대답에 간단합니다.

솔루션

단순히 MZ2의 제안 @ 구현했습니다 : 나는 Finder의 환경 설정을 가져 오는 방법으로 자신의 열거 예를 확장하고 파일에 속성을 설정하기 전에 올바른 지역화 된 라벨 색상의 이름을 추출했습니다.

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 [] 
    } 
} 

사용법 :

do { 
    // default English label names 
    try LabelColors.set(colors: [.yellow, .red], 
         to: fileURL) 

    // localized label names 
    let list = LabelColors.localizedLabelNames() 
    try LabelColors.set(colors: [.green, .blue], 
         to: fileURL, 
         using: list) 
} catch { 
    print("Error when setting label color(s): \(error)") 
} 
+0

이것을 구현하려 할 때 https://github.com/billgarrison/SOExtendedAttributes의 일부로 'setExtendedAttributeData'도 설치해야했습니다. 그러나 이것은 스위프트 모듈이 아니며이 언어로 시작합니다. 이 C 모듈을 사용하려면 무엇을해야합니까? 스위프트 전용 솔루션이 있습니까? –

+0

Xcode 프로젝트에 SOExtendedAttributes를 쉽게 통합 할 수 있습니다. (그리고 Swift에 대해 걱정할 필요가 없습니다.) zip을 다운로드 한 다음 "NSURL + SOExtendedAttributes.h"및 "NSURL + SOExtendedAttributes.m" 프로젝트 네비게이터에서 자신의 파일 옆에 있습니다. 나타나는 패널에서 "복사"상자를 선택해야합니다. 그런 다음 Xcode는 브리징 헤더를 만들 것인지 묻습니다. 네 라고 말하다. 그런 다음이 헤더에'#import "NSURL + SOExtendedAttributes.h"': 행을 추가하십시오. 이제'setExtendedAttributeData'를 사용할 수 있습니다. :) – Moritz

3

나는 색 이름을 알아야 할 필요없이 일하고 있어요 :

나는 실행 부분이 있지만 두 (독일어와 영어) 언어 .

0 없음
1 그레이
2 녹색
3 퍼플
4 블루
5 노란색
6 레드
7 오렌지 :이 태그 번호의 각 태그의 색을 나타낸다는 것을 알고

파일의 URL을 지정하십시오 :

var url = URL(fileURLWithPath: pathToYourFile) 

우리는 돌연변이가 있기 때문에 var이어야합니다.

새로운 URLResourceValues 인스턴스를 (또한 변수가 될 필요가있다) 만들기 :

var rv = URLResourceValues() 

이 같은 라벨 번호 설정 : 마지막으로

rv.labelNumber = 2 // green 

이 파일에 태그를 쓰기 :

do { 
    try url.setResourceValues(rv) 
} catch { 
    print(error.localizedDescription) 
} 

이 예에서는 숫자 태그를 2로 설정 했으므로 이제이 파일은 초록색으로 표시됩니다 색깔.

+0

니스! (사이드 노트 : 스위프트 3에서는'as NSError' 캐스트가 더 이상 필요 없습니다.) –

+0

이것은 아마 macOS 10.12에서만 작동합니까? –

+0

좋습니다. 하지만 한 가지 이상의 색상은 무엇입니까? .labelNumber에 하나의 0 - 7 값을 할당 할 수 있습니다.나는 그들을 추가 할 수 없으며 결합 할 수있는 비트도 아니다. –

4

단일 색상을 설정하려면 setResourceValue API 호출을 사용해야합니다. 그러나 사용해야하는 자원 키는 스위프트 3 (하지 NSURLTagNamesKey)에서 NSURLLabelNumberKey, 또는 URLResourceKey.labelNumberKey입니다 :

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의 스위프트 3 포트 엑스 코드 8.1, 맥 OS 시에라 10.12.1와 테스트입니다.)

여러 색상을 설정하려면 레이블 값으로 자원 값을 설정하는 데 사용한 API를 사용할 수 있습니다. 이 두 인코딩의 차이점은 다음과 같습니다. http://arstechnica.com/apple/2013/10/os-x-10-9/9/ - 기본적으로 레이블 키는 내부적으로 레이블 문자열의 배열을 이진 plist로 저장하는 확장 된 속성 "com.apple.metadata : _kMDItemUserTags"를 설정합니다. 단 하나의 색상 옵션은 표시됩니다 32 바이트 길이 확장 속성 값 "com.apple.FinderInfo"의 10 번째 바이트를 설정합니다.

해당 키 이름에서 "현지화 된"것은 사용자가 설정 한 레이블 이름 중에서 사용자가 선택한 레이블 집합이라는 의미에서 실제로 혼란스러운 점에서 다소 혼란 스럽습니다. 이러한 라벨 값은 실제로 현지화되었지만 처음 계정을 만들 때 현지화 설정에 따라 설정되는 범위까지만 표시됩니다. 데이터가 바이너리로 인코딩

➜ defaults read com.apple.Finder FavoriteTagNames 
(
    "", 
    Red, 
    Orange, 
    Yellow, 
    Green, 
    Blue, 
    Purple, 
    Gray 
) 

방법 : 설명하기 위해이 내가 테스트로 핀란드어 현지화로 설정하고 Finder를 다시 시작했던 내 시스템에 찾기가 사용하는 레이블 값, 재부팅 기계 등이다 plist 값은 단순히 배열에서 색인이 뒤 따르는 즐겨 찾는 태그 이름입니다 (실제 값은 1부터 시작하여 길이가 8로 고정됩니다. 즉 빨강, 주황, 노랑, 초록, 파랑, 자주색, 회색).

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 환경 설정에서 해당 키를 읽어야합니다 ('FavoriteTagNames'키). domain 'com.apple.Finder') 위와 같이 좋아하는 태그 이름의 배열을 인코딩합니다.

레이블 이름이 일 때 올바른지 확인하려는 경우 위의 사항을 무시하고 Finder 환경 설정 도메인에서 읽어야합니다 (앱이 샌드위치 여부에 따라 달라질 수도 있고하지 않을 수도 있음).

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)") 
} 
+1

실제로 확장 된 API를 기반으로 솔루션을 찾은 것처럼 보입니다. 이 특정 값은 응답에 대한 응답으로 제안 된 바이너리 plist가 아닙니다. 좀 더 주위를 돌아보며, 옵션 마스크로 이것을 포함하는 com.apple.FinderInfo 속성 값에 특정 2 바이트가 있는지 확인합니다. – mz2

+0

글쎄,이 같은 결론까지 : http://superuser.com/a/295155/157584 - 그 32 바이트 길이 확장 속성 값의 10 번째 바이트는 컬러 정보를 포함하고 있습니다. 나는 아직 그것을 아주 잘 읽고 있는지 확신하지 못한다. – mz2

+0

Nevermind, 해당 키에는이 자원 값 API로 조작 할 수있는 값이 들어 있습니다. 즉, 첫 번째로 설정된 색상 만 포함합니다. 색상 집합을 포함하는 키는 실제로 "com.apple.metadata : _kMDItemUserTags"이며 바이너리 plist를 포함합니다. – mz2

관련 문제