【问题标题】:How to use a NSNumberFormatter with an AttributedString?如何将 NSNumberFormatter 与 AttributedString 一起使用?
【发布时间】:2016-07-04 19:10:32
【问题描述】:

我找不到任何适用于在 NSTextFieldNumberFormatter 中使用属性文本的主题。我想要完成的非常简单。我想在带有属性文本的可编辑NSTextField 上使用NumberFormatter,并保留文本属性。

目前,我已将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]? = [:])

我已经分别尝试了这两种解决方案,也分别尝试过,但都没有成功。

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

【问题讨论】:

  • 什么对你不起作用?我试过这个(实际上简化了一点:gist.github.com/robertmryan/998a452a9a83eb4dbec4bebd2da52337),它似乎工作正常。我正在尝试了解您所经历的行为以及与您的预期有何不同?
  • @CodeDifferent 我正在使用 Swift 3。
  • @Rob 我期望的行为是NSTextField 中的文本将根据NumberFormatter 进行格式化,但保留其属性。它与我的预期不同,因为 NSTextField 中的文本已格式化但丢失了它的属性(即字体、段落样式等)。您发布的代码似乎对我不起作用。除了连接 @IBOutlet 之外,您在 Main.storyboard 文件中还做了什么?
  • 编辑时,属性样式不存在,但是当您离开文本字段并转到另一个文本字段时,属性文本样式会重新出现。如果您返回并编辑,格式会再次消失,直到您完成编辑。这是你看到的行为吗?
  • @Rob 仔细检查,格式保留在您的代码中,但在编辑 NSTextField 时不会。有没有办法在编辑NSTextField期间保留属性?

标签: swift cocoa nsattributedstring nstextfield nsnumberformatter


【解决方案1】:

对于将来想要实现行为的任何人,我可以通过子类化 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 以满足您的需要。在此实现中,“TextField”具有各种属性集,并且仅限于两个字符并且只有数字。

【讨论】:

  • @Rob 谢谢你的帮助。我找到了似乎可行的解决方案。
【解决方案2】:

您将 NSNumberFormatter 子类化为以下内容:

final class ChargiePercentageNumberFormatter: NumberFormatter {

    override func attributedString(for obj: Any, withDefaultAttributes attrs: [NSAttributedString.Key : Any]? = nil) -> NSAttributedString? {
        guard let obj = obj as? NSNumber else {
            return nil;
        }
        guard let numberString = self.string(from: obj) else {
            return nil
        }
        let font = NSFont(name: "Poppins-Regular", size: 16)!
        let attrs: [NSAttributedString.Key: Any] = [.font: font, .kern: 1.2]

        let result = NSAttributedString(string: numberString, attributes: attrs)
        return result
    }

}

【讨论】:

    猜你喜欢
    • 2021-01-02
    • 1970-01-01
    • 2020-01-29
    • 1970-01-01
    • 2023-03-13
    • 2014-09-25
    • 2016-01-30
    • 2015-12-13
    • 2020-09-15
    相关资源
    最近更新 更多