如何使用带有AttributedString的NSNumberFormatter?

时间:2016-07-04 19:10:33

标签: swift cocoa nsattributedstring nstextfield nsnumberformatter

我无法找到任何有关在NSTextField中使用NumberFormatter使用归因文字的主题。我想要完成的事情非常简单。我想对带有属性文字的可编辑NumberFormatter使用NSTextField,并保留文字归属。

目前,我已将NSTextFieldCell子类化并按原样执行:

class AdjustTextFieldCell: NSTextFieldCell {

  required init(coder: NSCoder) {
    super.init(coder: coder)
    let attributes = makeAttributes()
    allowsEditingTextAttributes = true
    attributedStringValue = AttributedString(string: stringValue, attributes: attributes)
    //formatter = TwoDigitFormatter()
  }

  func makeAttributes() -> [String: AnyObject] {
    let style = NSMutableParagraphStyle()
    style.minimumLineHeight = 100
    style.maximumLineHeight = 100
    style.paragraphSpacingBefore = 0
    style.paragraphSpacing = 0
    style.alignment = .center
    style.lineHeightMultiple = 1.0
    style.lineBreakMode = .byTruncatingTail
    let droidSansMono = NSFont(name: "DroidSansMono", size: 70)!
    return [NSParagraphStyleAttributeName: style, NSFontAttributeName: droidSansMono, NSBaselineOffsetAttributeName: -60]
  }


}

此实现调整NSTextField实例中的文本以显示所显示的属性。当我取消注释设置formatter属性的行时,NSTextField会丢失其属性。我的NumberFormatter如下:

class TwoDigitFormatter: NumberFormatter {

  override init() {
    super.init()
    let customAttribs = makeAttributes()
    textAttributesForNegativeValues = customAttribs.attribs
    textAttributesForPositiveValues = customAttribs.attribs
    textAttributesForZero = customAttribs.attribs
    textAttributesForNil = customAttribs.attribs
  }

  required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
  }

  let maxLength = 2
  let wrongCharacterSet = CharacterSet(charactersIn: "0123456789").inverted

  override func isPartialStringValid(_ partialString: String, newEditingString newString: AutoreleasingUnsafeMutablePointer<NSString?>?, errorDescription error: AutoreleasingUnsafeMutablePointer<NSString?>?) -> Bool {
    if partialString.characters.count > maxLength {
      return false
    }

    if partialString.rangeOfCharacter(from: wrongCharacterSet) != nil {
      return false
    }

    return true
  }

  override func attributedString(for obj: AnyObject, withDefaultAttributes attrs: [String : AnyObject]? = [:]) -> AttributedString? {
    let stringVal = string(for: obj)

    guard let string = stringVal else { return nil }
    let customAttribs = makeAttributes()
    var attributes = attrs
    attributes?[NSFontAttributeName] = customAttribs.font
    attributes?[NSParagraphStyleAttributeName] = customAttribs.style
    attributes?[NSBaselineOffsetAttributeName] = customAttribs.baselineOffset

    return AttributedString(string: string, attributes: attributes)

  }

  func makeAttributes() -> (font: NSFont, style: NSMutableParagraphStyle, baselineOffset: CGFloat, attribs: [String: AnyObject]) {
    let style = NSMutableParagraphStyle()
    style.minimumLineHeight = 100
    style.maximumLineHeight = 100
    style.paragraphSpacingBefore = 0
    style.paragraphSpacing = 0
    style.alignment = .center
    style.lineHeightMultiple = 1.0
    style.lineBreakMode = .byTruncatingTail
    let droidSansMono = NSFont(name: "DroidSansMono", size: 70)!
    return (droidSansMono, style, -60, [NSParagraphStyleAttributeName: style, NSFontAttributeName: droidSansMono, NSBaselineOffsetAttributeName: -60])
  }
}

正如您从上面的代码中可以看到的,我尝试过:

  1. 设置textAttributesFor...属性。
  2. 覆盖attributedString(for obj: AnyObject, withDefaultAttributes attrs: [String : AnyObject]? = [:])
  3. 我已经将这两种解决方案彼此分开试验,并且没有尝试过。

    TLDR
    是否可以同时使用属性文本和NumberFormatter?如果是这样,怎么样?如果没有,我如何限制NSTextField使用属性文本仅限数字而将两个字符限制为NumberFormatter

2 个答案:

答案 0 :(得分:0)

对于未来想要实现这种行为的人,我可以通过继承NSTextView并基本上伪造NSTextField来实现这一点:

class FakeTextField: NSTextView {

  required init?(coder: NSCoder) {
    super.init(coder: coder)

    delegate = self

    let droidSansMono = NSFont(name: "DroidSansMono", size: 70)!
    configure(lineHeight: frame.size.height, alignment: .center, font: droidSansMono)
  }

  func configure(lineHeight: CGFloat, alignment: NSTextAlignment, font: NSFont) {
    //Other Configuration

    //Define and set typing attributes
    let style = NSMutableParagraphStyle()
    style.minimumLineHeight = lineHeight
    style.maximumLineHeight = lineHeight
    style.alignment = alignment
    style.lineHeightMultiple = 1.0
    typingAttributes = [NSParagraphStyleAttributeName: style, NSFontAttributeName: font]
  }

}

extension TextField: NSTextViewDelegate {
  func textView(_ textView: NSTextView, shouldChangeTextIn affectedCharRange: NSRange, replacementString: String?) -> Bool {
    if let oldText = textView.string, let replacement = replacementString {
      let newText = (oldText as NSString).replacingCharacters(in: affectedCharRange, with: replacement)
      let numberOfChars = newText.characters.count
      let wrongCharacterSet = CharacterSet(charactersIn: "0123456789").inverted
      let containsWrongCharacters = newText.rangeOfCharacter(from: wrongCharacterSet) != nil
      return numberOfChars <= 2 && !containsWrongCharacters
    }
    return false
  }
}

使用此课程,您只需将故事板中的NSTextView设置为此课程,然后更改属性shouldChangeTextIn以满足您的需求。在这个实现中,&#34; TextField&#34;具有各种属性设置,限制为两个字符,仅限数字。

答案 1 :(得分:0)

您可以使用以下子类来继承NSNumberFormatter:

string