UITextField如何使用自定义字体显示安全输入框中的圆点?

40

我在UITextField中使用自定义字体,同时打开了secureTextEntry。当我在单元格中输入时,我可以看到我选择的字体中的黑点符号,但是当字段失去焦点时,这些黑点符号会回归系统标准字体。如果我再次点击该字段,则它们会重新变回我的字体,以此类推。

是否有一种方法可以确保即使在字段失去焦点时也继续显示自定义字体的黑点符号?

输入图像描述 输入图像描述


+1 好问题。我想知道那个 bug 是否已经向苹果报告了。 - Segev
3
我已经提交了一个问题报告。 - Luke
1
你不应该接受下面的https://dev59.com/MWEi5IYBdhLWcg3wweqq#22596030吗? - Geri Borbás
看起来我们已经有一个类似的问题了 https://dev59.com/g2Ik5IYBdhLWcg3wN7xj - Abhijeet
请看此链接:https://dev59.com/kIfca4cB1Zd3GeqPnMN8#33954746 - Daniel Galasko
适用于iOS 8和9,可能适用于7,但未经测试:https://dev59.com/kIfca4cB1Zd3GeqPnMN8#34777286 - Ramis
10个回答

18
一个子类解决这个问题。创建一个任意的UITextField,然后在IB中通过KVC将secure属性设置为YES
实际上,它是通过lukech建议的一个注释来实现的。当文本字段结束编辑时,它会切换到一个任意的文本字段,然后在其中输入一堆点,并且在text访问器中进行一些 hack,以始终获取该字段保存的实际文本。
@interface SecureTextFieldWithCustomFont : UITextField
@property (nonatomic) BOOL secure;
@property (nonatomic, strong) NSString *actualText;
@end


@implementation SecureTextFieldWithCustomFont


-(void)awakeFromNib
{
    [super awakeFromNib];

    if (self.secureTextEntry)
    {
        // Listen for changes.
        [self addTarget:self action:@selector(editingDidBegin) forControlEvents:UIControlEventEditingDidBegin];
        [self addTarget:self action:@selector(editingDidChange) forControlEvents:UIControlEventEditingChanged];
        [self addTarget:self action:@selector(editingDidFinish) forControlEvents:UIControlEventEditingDidEnd];
    }
}

-(NSString*)text
{
    if (self.editing || self.secure == NO)
    { return [super text]; }

    else
    { return self.actualText; }
}

-(void)editingDidBegin
{
    self.secureTextEntry = YES;
    self.text = self.actualText;
}

-(void)editingDidChange
{ self.actualText = self.text; }

-(void)editingDidFinish
{
    self.secureTextEntry = NO;
    self.actualText = self.text;
    self.text = [self dotPlaceholder];
}

-(NSString*)dotPlaceholder
{
    int index = 0;
    NSMutableString *dots = @"".mutableCopy;
    while (index < self.text.length)
    { [dots appendString:@"•"]; index++; }
    return dots;
}


@end

可能会被增强以适用于非NIB实例化,处理默认值等,但您可能已经理解了。


1
谢谢,这很好用。我在您的代码基础上做了一些改进:1)去掉了“secure”属性(我认为不需要,因为我假设此子类的所有文本字段都是安全的);2)避免了编辑更改更新;3)实现了适当的setText:方法。您可以在这里找到它:https://gist.github.com/rsanchezsaez/9836102 - Ricardo Sanchez-Saez
我记不清了,但我在安全文本字段(启动时)遇到了一些问题,所以我决定先从任意的 UITextFields 开始切换。 - Geri Borbás
2
我创建了一个UITextField类别来解决这个问题:https://github.com/elegion/ELFixSecureTextFieldFont(基于您的答案) - glyuck
1
@ glyuck,你的解决方案看起来更加复杂。为什么你觉得它更好? - André Fratelli
这个解决方案不是百分之百有效的。我发现发送到服务器的密码具有以下前导字符:“••”(它们很可能是点)。我猜测某些地方出了问题。 - Alberto M
哇,这个问题四年后仍然存在,有点奇怪。 - Geri Borbás

12

对于那些在切换secureTextEntry时遇到自定义字体丢失问题的人,我发现了一个解决方法(我正在使用iOS 8.4 SDK)。我试图为UITextField创建一个显示/隐藏密码的切换。每次我切换secureTextEntry = NO时,我的自定义字体都会出错,只有最后一个字符显示了正确的字体。这方面肯定出了点问题,但是这是我的解决方案:

-(void)showPassword {
    [self.textField resignFirstResponder];
    self.textField.secureTextEntry = NO;
}

由于某些原因,第一响应者需要重新设计。当将secureTextEntry设置为YES时,似乎不需要重新设计第一响应者,只有在设置为NO时才需要。


9
实际上,问题似乎在于编辑视图(UITextField)在编辑时没有显示其自己的文本,而是使用圆点符号(U+2022)来绘制被编辑字符,而UITextField则使用黑色圆圈(U+25CF)。我想,在默认字体下,这些字符看起来相同。
如果你感兴趣,这里有一个备选方案,它使用了自定义的文本框子类,但不需要搞什么特殊配置或文本属性操作。在我看来,这样相对比较简洁明了。
@interface MyTextField : UITextField
@end

@implementation MyTextField

- (void)drawTextInRect:(CGRect)rect
{
    if (self.isSecureTextEntry)
    {
        NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];
        paragraphStyle.alignment = self.textAlignment;

        NSMutableDictionary *attributes = [NSMutableDictionary dictionary];
        [attributes setValue:self.font forKey:NSFontAttributeName];
        [attributes setValue:self.textColor forKey:NSForegroundColorAttributeName];
        [attributes setValue:paragraphStyle forKey:NSParagraphStyleAttributeName];

        CGSize textSize = [self.text sizeWithAttributes:attributes];

        rect = CGRectInset(rect, 0, (CGRectGetHeight(rect) - textSize.height) * 0.5);
        rect.origin.y = floorf(rect.origin.y);

        NSMutableString *redactedText = [NSMutableString new];
        while (redactedText.length < self.text.length)
        {
            [redactedText appendString:@"\u2022"];
        }

        [redactedText drawInRect:rect withAttributes:attributes];
    }
    else
    {
        [super drawTextInRect:rect];
    }
}

@end

这个方法非常好用,谢谢!唯一的缺点是在进入和退出编辑模式时,项目符号会有些跳动。可以通过硬编码rect.origin.y来解决,但也许有更简洁的解决方案? - maxkonovalov
哦,我的情况是如果我省略了 rect.origin.y = floorf(rect.origin.y) 语句就会出现跳跃。使用 roundf() 可能比使用 floorf() 更好。 - Austin
我明白了,但在我的情况下,我必须将origin.y设置为1.5,因此这可能会因文本字体和大小而异... - maxkonovalov
3
drawTextInRect方法从未被调用。 - Tapan Thaker
@Austin 做得很好!我喜欢你的解决方案比 Geri 的更好。干得好! - mokagio

3
虽然这是一个iOS的bug(并且是iOS 7中新增的),但我有另一种方法可以解决这个问题,可能会更加可接受。尽管功能略微降低,但并不明显。基本上,想法是每当字段输入内容时将字体设置为默认字体系列/样式;但是当没有输入内容时,将其设置为自定义字体。(字体大小可以保持不变,因为字体系列/样式而不是大小存在问题。)捕获字段值的每次更改,并在此时相应地设置字体。当未输入任何内容时的淡色"提示"文本具有您想要的字体(自定义);但是当输入任何内容(无论您是否正在编辑)时,都会使用默认字体(Helvetica)。因为项目符号始终是项目符号,所以这看起来很好。唯一的缺点是在被替换为项目符号之前输入的字符将使用默认字体(Helvetica)。不过每个字符只有短短的一瞬间。如果可以接受这一点,那么这个解决方案就可行。

1
[passWordTextField resignFirstResponder];
passWordTextField.secureTextEntry = !passWordTextField.secureTextEntry;
[passWordTextField becomeFirstResponder];

这是解决此错误的最快方法!


当它再次成为第一响应者并且用户继续输入时,之前输入的所有内容都将被清除。 - Patrick Lynch
代码只有在用户开始输入时才运行,但不是在键入时运行。因此没有东西需要清除。 - AaronTKD

1

我发现了解决这个问题的诀窍。

- (void)textFieldDidBeginEditing:(UITextField *)textField
{
   if ([textField tag]== TAG_PASS || [textField tag]== TAG_CPASS)
    {
       // To fix password dot size
        if ([[textField text] isEqualToString:@"" ])
        {
          [textField setText:@" "];
          [textField resignFirstResponder];
          [textField becomeFirstResponder];
          [textField setText:@""];
        }  
    }
}

这也可以很容易地在此方法之外完成。好发现,Parul。 - Damien Justin Šutevski

0

可以完全避免使用secureTextEntry文本字段:

NSString *pin = @"";
BOOL pasting = FALSE;
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
    if(!pasting) {
        pin = [pin stringByReplacingCharactersInRange:range withString:string];

        // Bail out when deleting a character
        if([string length] == 0) {
            return YES;
        }

        pasting = TRUE;
        [textField paste:@"●"];

        return NO;
    } else {
        pasting = FALSE;
        return YES;
    }
}

0

Swift 5和iOS 14已经发布,但是对于自定义字体,即使将isSecureTextEntry设置为true,仍然会显示错误大小的圆点,尽管实际上前导字母的大小是正确的。

除了一种hacky的解决方法——在密码处于安全模式时将字体设置为系统字体之外,我从stackoverflow上找到的所有解决方案都没有起作用。

  if textField.isSecureTextEntry {
            self.textField.font = UIFont.systemFont(ofSize: 17)
    } else {
        self.textField.font = UIFont(name: "Roboto-Regular", size: 17)
    }

0

iOS在使用自定义字体时有些奇怪。尝试为该文本字段取消“自适应大小”选项。如果这不起作用,我猜你困扰的是字体大小的增加。

一个简单的解决方案是:

- (void)textFieldDidBeginEditing:(UITextField *)textField
{
    if(textField.secureTextEntry)
    {
        [textField setFont:[UIFont fontWithName:@"Helvetica-Bold" size:10.0]];
    }
}

-(void)textFieldDidEndEditing:(UITextField *)textField
{
    if(textField.secureTextEntry)
    {
        [textField setFont:[UIFont fontWithName:@"Helvetica-Bold" size:10.0]];
    }
}

为了让UITextField失去焦点时看起来没有大小变化,您需要稍微调整一下大小。

如果您在字符之间有严重的间距问题,就像编辑后的问题一样,最简单(但有点丑陋)的解决方案是创建一个符合上述大小和间距的子弹图像,并与用户输入的字符数量相匹配,当用户离开UITextField时将显示这些字符。


无论我使用什么自定义字体,无论是TTF、OTF等格式,都会出现这种情况。我已经尝试了几种字体,但是“调整大小以适应”也没有任何作用,很遗憾:( - Luke
很抱歉,这没有任何区别。每当我离开文本字段时,字体仍会恢复为系统标准。 - Luke
@lukech 上面的代码对我尝试过的每种自定义字体都可以正常工作。请确保 UITextField 有一个委托,并且上述方法正在被调用。 - Segev
2
我认为最简单的方法就是在 didEndEditing 点关闭 secureTextEntry,然后创建一个包含正确数量的圆点的字符串,将其设置为密码框的文本,并单独跟踪实际密码,根据需要进行交换。 - Luke
哇,我要试一下这个。 - Geri Borbás
显示剩余8条评论

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接