2013-02-25 57 views
2

我需要在OS X Cocoa中创建一个视图,该视图隐藏除最后输入的字符以外的所有内容(直到该字符在设定的时间后隐藏)。基本上我想在OS X Cocoa中使用UITextFieldtextfield.secureTextEntry = YES在OS X中实现iOS密码输入视图Cocoa

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

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

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

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

我还会说,我有强烈的偏见,不推出我自己的实施。如果有人知道我可以利用之前的工作方式,或者只是将UITextField导入到OS X Cocoa来解决这个问题,我会欢迎(并且非常青睐)这些解决方案。

+0

嗯,我对这个想法有点分歧。 UITextField的行为方式之所以如此,是因为屏幕键盘比普通桌面键盘更难使用(较小的键盘尺寸,没有触觉反馈等)。在OS X上有什么目的来重现这一点? (不包括你的问题,我只是好奇) – dreamlax 2013-02-26 00:37:46

+0

你可以继承'NSTextField'并提供一个自定义的'drawRect:',但我想你可能实际上需要提供一个自定义的'NSTextFieldCell',并覆盖它的'drawRect: inView:'方法。 – dreamlax 2013-02-26 00:39:36

回答

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))))))