2

我需要在 OS X Cocoa 中创建一个视图,该视图隐藏除最后输入的字符之外的所有字符(直到该字符在一段时间后被隐藏)。基本上我想在 OS X Cocoa中使用UITextFieldwith 。textfield.secureTextEntry = YES

我找不到UITextField在 OS X Cocoa 中导入的方法。无论如何,我认为这是不可能的,因为NSTextFieldandUITextField来自两个不同的框架。

NSSecureTextField在 OS X Cocoa 中,最后一个字符不会在一定时间内保持可见,而且我找不到可以设置的实例或类属性来改变这种行为。

我尝试推出自己的实现,从 开始NSTextField并触发事件keyUp:,但我遇到了极端情况的问题。我目前的方法是将存储的文本更改NSTextField为隐藏字符(除了最后一个字符之外的所有字符,持续一段时间等)。然而,使用这种技术,当用户选择 7 个字符密码中的第 2 到第 5 个字符并删除它们时会发生什么。那么我怎样才能确定哪些隐藏字符被删除,以便我可以检索正确输入的密码?

所以我认为如果我需要推出自己的实现,我不需要更改存储在 中的实际文本NSTextField,而是更改视图在屏幕上的显示方式。但是我目前找不到这样做的方法。在这里的帮助将不胜感激。

我还要说我有强烈的偏见不推出自己的实现。如果有人知道我可以利用以前的工作或简单地导入UITextFieldOS X Cocoa 来解决这个问题的方法,我会欢迎(并强烈支持)这些解决方案。

4

1 回答 1

1

使用NSTextField,我无法成功地将文本的绘制与存储的文本分开。我尝试自定义NSFormatter并简要了解自定义字段编辑器。我得到的最接近的是通过自定义drawInteriorWithFrame:inView:. 这让我可以控制绘图,但仅限于字段编辑器无法控制对象时(即,仅当不编辑对象中的文本时)。对于我正在尝试做的事情,所有方法似乎都过于复杂。

使用NSTextView我能够让它工作。方法是将绘制的字符与存储的字符分开,并将实际的文本存储在对象中。绘图方法在需要隐藏文本时简单地使用星号,这不会影响存储在对象中的文本值。

这是一些使用这种方法的代码。它在 Clozure Common Lisp 中实现,通过 Clozure 的 Objective C 桥与 Cocoa 通信:

(defclass easygui::cocoa-password-entry-text-view (easygui::cocoa-text-view)
  ((pending-fun :accessor pending-fun :initform nil)
   (visible-char-time-secs :reader visible-char-time-secs :initform 1))
  (:metaclass ns:+ns-object))

(defclass easygui::cocoa-password-entry-layout-manager (ns:ns-layout-manager)
  ((last-char-vis-p :accessor last-char-vis-p :initform nil))
  (:metaclass ns:+ns-object))

(defclass password-entry-text-view (text-view)
  ()
  (:default-initargs :specifically 'easygui::cocoa-password-entry-text-view))

(objc:defmethod #/initWithFrame: ((self easygui::cocoa-password-entry-text-view) (frame #>NSRect))
  (unwind-protect (call-next-method frame)
    (#/replaceLayoutManager: (#/textContainer self)
     (#/init (#/alloc easygui::cocoa-password-entry-layout-manager)))
    (#/setFont: self
     (convert-font "Courier" 12))))

(objc:defmethod (#/drawGlyphsForGlyphRange:atPoint: :void) ((self easygui::cocoa-password-entry-layout-manager) (glyph-range #>NSRange) (at-point #>NSPoint))
  (let ((glyph-cnt (#/numberOfGlyphs self)))
    (let ((hide-until (if (last-char-vis-p self) (1- glyph-cnt) glyph-cnt)))
      (dotimes (i hide-until)
        (#/replaceGlyphAtIndex:withGlyph: self i 13))))
  (call-next-method glyph-range at-point))

(defmethod dialog-item-hidden-text ((view password-entry-text-view))
  (let ((text (dialog-item-text view)))
    (let ((layout-manager (#/layoutManager (cocoa-ref view))))
      (with-output-to-string (strm)
        (dotimes (i (1- (length text)))
          (format strm "*"))
        (format strm "~a" (if (last-char-vis-p layout-manager)
                            (char text (1- (length text)))
                            "*"))))))

(defmethod cursor-at-end-of-text-p ((cocoa-self easygui::cocoa-password-entry-text-view))
  (awhen (#/selectedRanges cocoa-self)
    (when (eq (#/count it) 1)
      (awhen (#/rangeValue (#/objectAtIndex: it 0))
        (let ((pos (ns:ns-range-location it)))
          (let ((length (ns:ns-range-length it)))
            (when (eq length 0)
              (when (eq pos (#/length (#/string cocoa-self)))
                t))))))))

(objc:defmethod (#/keyDown: :void) ((cocoa-self easygui::cocoa-password-entry-text-view) the-event)
  (call-next-method the-event)
  (labels ((get-keypress (the-event)
             (let* ((chars (#/characters the-event))
                    (str (objc:lisp-string-from-nsstring chars))
                    (char (char str 0)))
               char)))
    (handle-keypress-on-view
      (easygui::easygui-view-of cocoa-self)
      (get-keypress the-event))))

(defmethod handle-keypress-on-view ((view password-entry-text-view) keypress)
  (let ((cocoa-self (cocoa-ref view)))
    (cond ((or (eq keypress #\rubout)
               (not (cursor-at-end-of-text-p cocoa-self)))
           (setf (last-char-vis-p (#/layoutManager cocoa-self)) nil))
          (t
           (setf (last-char-vis-p (#/layoutManager cocoa-self)) t)
           (setf (pending-fun cocoa-self)
                 (alambda ()
                   (when (eq #'self (pending-fun cocoa-self))
                     (setf (last-char-vis-p (#/layoutManager cocoa-self)) nil)
                     (#/setNeedsDisplay: cocoa-self #$YES))))
           (schedule-for-event-process
             (pending-fun cocoa-self)
             (visible-char-time-secs cocoa-self))))))
于 2013-02-28T23:22:45.460 回答