ซ่อนเคอร์เซอร์ของ UITextField


145

ฉันใช้ a UITextFieldกับUIPickerViewสำหรับมันinputViewดังนั้นเมื่อผู้ใช้แตะที่ช่องข้อความตัวเลือกจะถูกเรียกเพื่อให้พวกเขาเลือกตัวเลือกจาก

เกือบทุกอย่างใช้งานได้ แต่ฉันมีปัญหาอย่างหนึ่ง: เคอร์เซอร์ยังคงกะพริบในช่องข้อความเมื่อมีการใช้งานซึ่งน่าเกลียดและไม่เหมาะสมเนื่องจากผู้ใช้ไม่คาดว่าจะพิมพ์ลงในฟิลด์และไม่ได้แสดงด้วยแป้นพิมพ์ ฉันรู้ว่าฉันสามารถแก้ปัญหานี้ได้โดยแฮ็กโดยการตั้งค่าeditingเป็นNOบนฟิลด์ข้อความและการติดตามการสัมผัสหรือโดยแทนที่ด้วยปุ่มสไตล์ที่กำหนดเองและเรียกตัวเลือกผ่านรหัส อย่างไรก็ตามฉันต้องการใช้UITextFieldDelegateวิธีการสำหรับการจัดการเหตุการณ์ทั้งหมดในช่องข้อความและการแฮ็กเช่นการแทนที่ช่องข้อความด้วยปุ่มไม่อนุญาตให้ใช้วิธีนี้

ฉันจะซ่อนเคอร์เซอร์บนUITextFieldแทนได้อย่างไร

คำตอบ:


279

เพียงแค่คลาสย่อย UITextField และแทนที่ caretRectForPosition

- (CGRect)caretRectForPosition:(UITextPosition *)position
{
    return CGRectZero;
}

2
สวย! ใช้งานได้เหมือนมีเสน่ห์สำหรับฉัน
Joe Strout

1
@ โจเซฟชิวทำงานได้ดีมาก แต่ไม่ใช่ใน iOS 4.3 คุณช่วยฉันได้ไหม
Dinesh Raja

แนวทางที่สะอาดและสั้นที่สุดซึ่งสมควรได้รับการ
โหวต

1
เพียงแค่ทราบ ในคลาสย่อยของฉันฉันเพิ่มบูล hideCaret จากนั้นในการแทนที่นี้ถ้ามันเป็นจริง -> ส่งคืน CGRectZero อื่น ๆ จะส่งคืนผลลัพธ์ของ super
WCByrne

6
โปรดทราบว่าผู้ใช้ที่มีแป้นพิมพ์ภายนอกสามารถเปลี่ยนค่าของช่องข้อความได้แม้ว่าเคอร์เซอร์จะซ่อนอยู่และคุณใช้มุมมองเครื่องมือเลือก
ทำเครื่องหมาย

160

สำหรับ iOS 7 ตอนนี้คุณสามารถตั้งค่าtintColor = [UIColor clearColor]บน textField ได้แล้วเครื่องหมายรูปหมวกจะหายไป


1
สิ่งนี้ใช้ได้ผลในขณะนี้อย่างไรก็ตามฉันไม่แนะนำให้ใช้เนื่องจากอาจมีการเปลี่ยนแปลงในอนาคต แทนที่จะเลือกใช้caretRectForPosition:โซลูชันการลบล้าง
lipka

@lipka True นั่นน่าจะเป็นวิธีที่ดีกว่า
jamone

2
ดีพอสำหรับตอนนี้ บางครั้งคุณก็ต้องการวิธีแก้ปัญหาอย่างรวดเร็ว
GoldenJoe

นี่เป็นวิธีแก้ปัญหาที่ง่ายที่สุด!
Jay Q.

1
คำตอบนี้ควรได้รับการอนุมัติสำหรับ iOS 7+
tryp

100

คุณสามารถล้าง tintColor ของฟิลด์ข้อความได้

self.textField.tintColor = [UIColor clearColor];

Swift 3.0

self.textField.tintColor = .clear

ป้อนคำอธิบายภาพที่นี่


คำตอบที่ง่ายที่สุด
Nik Kov

2
ดังที่ได้กล่าวมาแล้วการล้างสีอ่อนไม่ได้เป็นการหยุดผู้ใช้ที่มีแป้นพิมพ์ภายนอก (iPad Pro) จากการเปลี่ยนข้อความ
Michael Long

@ ไมเคิลยาวนี้ไม่ได้เกี่ยวกับการหยุดผู้ใช้จากการเปลี่ยนข้อความ แต่เป็นเพียงตัวเลือกสไตล์เท่านั้น
JRam13

21

คุณอาจต้องการหยุดผู้ใช้จากการเลือกคัดลอกหรือวางข้อความใด ๆ เพื่อให้การป้อนข้อความเพียงรายการเดียวมาจากมุมมองตัวเลือก

- (CGRect) caretRectForPosition:(UITextPosition*) position
{
    return CGRectZero;
}

- (NSArray *)selectionRectsForRange:(UITextRange *)range
{
    return nil;
}

- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
{
    if (action == @selector(copy:) || action == @selector(selectAll:) || action == @selector(paste:))
    {
        returnNO;
    }

    return [super canPerformAction:action withSender:sender];
}

http://b2cloud.com.au/tutorial/disabling-the-caret-and-text-entry-in-uitextfields/


15

ตรวจสอบคุณสมบัติ selectedTextRangeของโปรโตคอล UITextInputที่คลาสเป็น UITextFieldไปตาม ฟิ้ว! นั่นคือบทเรียนในการเขียนโปรแกรมเชิงวัตถุตรงนั้น

ซ่อน Caret

หากต้องการซ่อนเครื่องหมายคาเร็ตให้ปิดช่วงข้อความที่เลือกของช่องข้อความ

textField.selectedTextRange = nil; // hides caret

ยกเลิกการซ่อน Caret

มีสองวิธีในการยกเลิกการซ่อนคาเร็ต

  1. ตั้งค่าช่วงข้อความที่เลือกของช่องข้อความไว้ที่ส่วนท้ายของเอกสาร

    UITextPosition *end = textField.endOfDocument;
    textField.selectedTextRange = [textField textRangeFromPosition:end
                                                        toPosition:end];
    
  2. เพื่อให้คาเร็ตอยู่ในจุดเดียวกันก่อนอื่นให้จัดเก็บช่วงข้อความที่เลือกของฟิลด์ข้อความไปยังตัวแปรอินสแตนซ์

    _textFieldSelectedTextRange = textField.selectedTextRange;
    textField.selectedTextRange = nil; // hides caret
    

    จากนั้นเมื่อคุณต้องการยกเลิกการซ่อนเครื่องหมายคาเร็ตเพียงแค่ตั้งค่าช่วงข้อความที่เลือกของช่องข้อความกลับไปเป็นแบบเดิม:

    textField.selectedTextRange     = _textFieldSelectedTextRange;
    _textFieldLastSelectedTextRange = nil;
    

4
โซลูชันเฉพาะนี้ใช้ไม่ได้กับการใช้งานของฉัน เคอร์เซอร์ยังคงกะพริบ
Art Geigel

ถ้าอย่างนั้นบางทีคุณควรส่งข้อบกพร่องที่bugreport.apple.comเนื่องจากเอกสารของ iOS ระบุว่า: "หากช่วงข้อความมีความยาวแสดงว่าข้อความที่เลือกในปัจจุบันหากมีความยาวเป็นศูนย์แสดงว่าเครื่องหมายคาเร็ต (insertion จุด) หากวัตถุช่วงข้อความเป็นศูนย์แสดงว่าไม่มีการเลือกปัจจุบัน "
ma11hew28

4
ฉันไม่สนใจมากพอที่จะยื่นรายงาน หากคนอื่นใช้ "โซลูชัน" ของคุณและไม่เห็นว่ามันได้ผลฉันอยากให้พวกเขารู้ว่าพวกเขาไม่ได้อยู่คนเดียว
Art Geigel

แม้จะมีความคิดเห็นของ @ ArtGeigel แต่มันก็ใช้ได้ดีสำหรับฉัน อย่างไรก็ตามฉันชอบวิธีแก้ปัญหาที่เกี่ยวข้องกับการลบล้างcaretRectForPositionมากกว่า มีความชัดเจนมากขึ้นว่ากำลังทำอะไรอยู่และเอกสารที่คุณยกมาก็ไม่ได้ระบุชัดเจนว่าพฤติกรรมของคาเร็ตควรเป็นอย่างไรเมื่อไม่มี 'ไม่มีการเลือกปัจจุบัน' ถ้า @ ArtGeigel อ้างว่าสิ่งนี้ไม่ถูกต้อง (ซึ่งก็ไม่ได้อย่างน้อยที่สุดเท่าที่ฉันเห็น) มันจะไม่ชัดเจนว่านั่นเป็นข้อบกพร่อง
Mark Amery

ไม่ได้ผลสำหรับฉันเช่นกัน คาเร็ตยังคงอยู่ที่นั่นและกระพริบตา
CW0007007

11

คำตอบจาก OP คัดลอกมาจากเนื้อหาคำถามเพื่อช่วยทำความสะอาดส่วนท้ายของคำถามที่ยังไม่ได้ตอบ

ฉันพบวิธีแก้ปัญหาอื่น: คลาสย่อยUIButtonและแทนที่วิธีการเหล่านี้

- (UIView *)inputView {
    return inputView_;
}

- (void)setInputView:(UIView *)anInputView {
    if (inputView_ != anInputView) {
        [inputView_ release];
        inputView_ = [anInputView retain];
    }
}

- (BOOL)canBecomeFirstResponder {
    return YES;
}

ตอนนี้ปุ่มUIResponderมีลักษณะการทำงานคล้ายกันมากกว่าUITextFieldและการใช้งานค่อนข้างตรงไปตรงมา


5
นี่ไม่ใช่ทางออกที่ดีจริงๆ ตรวจสอบคำตอบด้านล่างนี้: stackoverflow.com/a/13660503/1103584
DiscDev

2
เหตุใดจึงไม่เป็นทางออกที่ดี บรรลุผลตามที่ตั้งใจไว้และยังมอบฟังก์ชันการทำงานให้กับคลาสที่ก่อนหน้านี้ไม่มี นอกจากนี้ยังไม่ใช่การแฮ็ก ผมคิดว่ามันเยี่ยม. แล้วมันผิดอะไร?
BreadicalMD

2
@BreadicalMD ปัญหาใหญ่ที่สุดที่ฉันเห็นคือคุณไม่สามารถใช้ a UITextFieldDelegateกับสิ่งนี้เพื่อจัดการเหตุการณ์เริ่มต้นและสิ้นสุดการแก้ไข แทน - เว้นแต่จะมีวิธีจัดการกับเหตุการณ์ที่ฉันไม่รู้ - คุณจะต้องลบล้างbecomeFirstResponderและresignFirstResponderในคลาสย่อยของปุ่มและอาจสร้างโปรโตคอลตัวแทนของคุณเองเพิ่มในdelegateคุณสมบัติและเรียกผู้รับมอบสิทธิ์จากสิ่งที่กล่าวมา วิธีการ ทั้งหมดนี้ได้ผลมากกว่าการลบล้างcaretRectForPositionในUITextFieldคลาสย่อย
Mark Amery

1
@BreadicalMD ที่กล่าวว่าแม้ว่าคำตอบของโจเซฟชิวจะเหนือกว่าในแง่มุมที่ใช้งานได้จริง แต่ฉันก็ยังเห็นด้วยกับคุณว่านี่มันเซ็กซี่ ฉันไม่เคยดูการUIResponderอ้างอิงชั้นเรียนอย่างละเอียดมาก่อนและไม่รู้ว่ากลอุบายแบบนี้เป็นไปได้
Mark Amery

สายไปงานปาร์ตี้ แต่ฉันคิดว่านี่เป็นวิธีแก้ปัญหาที่ดีมากและรู้สึกว่าเหมาะสมกว่าและแฮ็คน้อยกว่าการใช้UITextFieldเคอร์เซอร์และซ่อนซึ่งโดยพื้นฐานแล้วเป็นการแฮ็กเนื่องจากเราใช้ช่องข้อความเป็นป้ายกำกับในขณะที่ ไม่ได้ใช้ฟังก์ชันใด ๆ ของไฟล์UITextField.
Rupert

7

โพสต์ของ Swift 5 เวอร์ชัน Net

  override func caretRect(for position: UITextPosition) -> CGRect {
    return .zero
  }
  
  override func selectionRects(for range: UITextRange) -> [UITextSelectionRect] {
    return []
  }
  
  override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
    return false
  }

ในกรณีของฉันมันใช้งานได้ เพิ่มคลาสย่อยของ UITextField ด้วยวิธีการเหล่านี้โดยใช้ Swift 4 ขอบคุณ!
J. Fdez

3

ตั้งค่า tintColor เป็น Clear Color

textfield.tintColor = [UIColor clearColor];

และคุณยังสามารถตั้งค่าจากตัวสร้างอินเทอร์เฟซ


2
คุณเพิ่งคัดลอกคำตอบเมื่อ 2 ปีที่แล้ว
Ashley Mills

1
ขอโทษเพื่อนของฉัน แต่ฉันไม่ได้คัดลอกอะไรเลย
Ahmad Al-Attal

4
ที่น่าสนใจ "เพื่อนของฉัน" คำตอบของคุณดูใกล้เคียงกับคำตอบของ @ oldman ตั้งแต่วันที่ 1 พ.ค. 58 บอกได้ไหมว่าของคุณแตกต่างกันอย่างไร?
Ashley Mills

1
ดังที่ได้กล่าวมาแล้วการล้างสีอ่อนไม่ได้เป็นการหยุดผู้ใช้ที่มีแป้นพิมพ์ภายนอก (iPad Pro) จากการเปลี่ยนข้อความ
Michael Long

ผู้ใช้มืออาชีพสามารถทำสิ่งที่ต้องการได้ พวกเขาเป็นผู้เชี่ยวชาญ: พวกเขารู้ว่ากำลังทำอะไร; ^)
Anton Tropashko

2

หากคุณต้องการซ่อนเคอร์เซอร์คุณสามารถใช้สิ่งนี้ได้อย่างง่ายดาย! มันได้ผลสำหรับฉัน ..

[[textField valueForKey:@"textInputTraits"] setValue:[UIColor clearColor] forKey:@"insertionPointColor"]

3
นี่เป็นสิ่งที่ไม่มีเอกสารเท่าที่ฉันรู้ เป็นไปได้ว่าการใช้สิ่งนี้จะทำให้แอปของคุณถูกปฏิเสธไม่ให้เรียก API ส่วนตัวหากคุณส่งไปยังร้านแอปแม้ว่าฉันจะไม่ทราบว่ามีการส่งใด ๆ โดยใช้สิ่งนี้เพื่อทดสอบการเก็งกำไรไม่ทางใดก็ทางหนึ่ง น่าเสียดายเพราะเป็นเรื่องดีที่สามารถแก้ปัญหานี้ได้โดยไม่ต้องมีคลาสย่อยและคำตอบนี้ก็ช่วยให้สามารถทำได้
Mark Amery

1

คำตอบจาก OP คัดลอกมาจากเนื้อหาคำถามเพื่อช่วยทำความสะอาดส่วนท้ายของคำถามที่ยังไม่ได้ตอบ

ฉันคิดว่าฉันมีวิธีแก้ไขที่ถูกต้อง แต่ถ้าสามารถปรับปรุงได้จะยินดีต้อนรับ :) ฉันสร้างคลาสย่อยของ UITextField และลบล้างเมธอดที่ส่งคืน CGRect สำหรับขอบเขต

-(CGRect)textRectForBounds:(CGRect)bounds {
    return CGRectZero;
}

ปัญหา? ข้อความไม่แสดงเนื่องจาก rect เป็นศูนย์ แต่ฉันเพิ่ม UILabel เป็นมุมมองย่อยของตัวควบคุมและแทนที่เมธอด setText ดังนั้นเมื่อเราป้อนข้อความตามปกติข้อความฟิลด์ข้อความจะเป็นศูนย์และเป็นป้ายกำกับที่แสดงข้อความ

- (void)setText:(NSString *)aText {
    [super setText:nil];

    if (aText == nil) {
        textLabel_.text = nil;
    }

    if (![aText isEqualToString:@""]) {
        textLabel_.text = aText;
    }
}

ด้วยเหตุนี้สิ่งนี้จึงได้ผลตามที่คาดไว้ คุณรู้วิธีปรับปรุงหรือไม่?


คำตอบนี้ไร้ค่าโดยพื้นฐานแล้วเนื่องจากแนวทางอื่นของ Joseph Chiu นั้นคล้ายกันมาก แต่ง่ายกว่ามาก ฉันขอแนะนำให้ลบออกได้ไหม
Mark Amery

1

ในการปิดใช้งานเคอร์เซอร์และเมนูฉันใช้คลาสย่อยด้วย 2 วิธีต่อไปนี้:

- (CGRect)caretRectForPosition:(UITextPosition *)position {
    return CGRectZero;
}

- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
    [UIMenuController sharedMenuController].menuVisible = NO;
    self.selectedTextRange = nil;

    return NO;
}

0

ฉันเพียงแค่คลาสย่อยUITextFieldและลบล้างlayoutSubviewsดังนี้:

- (void)layoutSubviews
{
    [super layoutSubviews];
    for (UIView *v in self.subviews)
    {
        if ([[[v class] description] rangeOfString:@"UITextSelectionView"].location != NSNotFound)
        {
            v.hidden = YES;
        }
    }
}

เป็นการแฮ็กที่สกปรกและอาจล้มเหลวในอนาคต (ณ จุดนี้เคอร์เซอร์จะปรากฏขึ้นอีกครั้ง - แอปของคุณจะไม่ขัดข้อง) แต่ใช้งานได้


-1

คุณสามารถเพิ่มBOOL cursorlessคุณสมบัติUITextFieldในหมวดหมู่ผ่านวัตถุที่เกี่ยวข้อง

@interface UITextField (Cursorless)

@property (nonatomic, assign) BOOL cursorless;

@end

จากนั้นใช้วิธีการที่จะ swizzling swizzle caretRectForPosition:ด้วยวิธีการที่สลับระหว่างและค่าเริ่มต้นของการใช้CGRectZerocursorless

สิ่งนี้นำไปสู่อินเทอร์เฟซที่เรียบง่ายผ่านหมวดหมู่แบบเลื่อนลง สิ่งนี้แสดงให้เห็นในไฟล์ต่อไปนี้

เพียงวางลงในอินเทอร์เฟซที่เรียบง่ายนี้

UITextFieldหมวดหมู่: https://github.com/rexmas/RexDK/blob/master/RexDK/UI/UITextField%2BRXCursorless.h https://github.com/rexmas/RexDK/blob/master/RexDK/UI/UITextField%2BRXCursorless .m

วิธีการ Swizzling: https://github.com/rexmas/RexDK/blob/master/RexDK/Foundation/NSObject%2BRXRuntimeAdditions.h https://github.com/rexmas/RexDK/blob/master/RexDK/Foundation/NSObject% 2BRXRuntimeAdditions.m


ทางออกที่ไม่ดีในหลาย ๆ ระดับ! สำหรับผู้เริ่มต้น: อย่าแทนที่วิธีการในหมวดหมู่ หลีกเลี่ยง Method Swizziling เว้นแต่คุณจะต้องการจริงๆ (คนอื่น ๆ มีวิธีแก้ปัญหาที่ดีสำหรับคำถามนี้) มันทำให้โค้ดของคุณซับซ้อนขึ้นมาก
EsbenB

หากคุณดูโค้ดจริงๆแล้วคุณจะรู้ว่าไม่มีวิธีใดถูกแทนที่ในหมวดหมู่และการใช้วิธี swizzling อย่างถูกต้อง ฉันใช้การใช้งานนี้มาหลายปีแล้วโดยไม่ล้มเหลว
Awesome-o

อธิบายว่ามีอะไรซับซ้อนเกี่ยวกับการเพิ่มฐานรหัสแยกเป็น ~ 150 บรรทัดเพื่อแก้ปัญหาที่เกิดซ้ำอย่างถาวร? สิ่งนี้ไม่เพียง แต่จะไม่ทำให้ codebase ของคุณซับซ้อน แต่ยังให้อินเทอร์เฟซที่ง่ายที่สุด บูลีนเดียวเพื่อกำหนดฟังก์ชันการทำงาน
Awesome-o

ลองดูการสนทนาที่ยอดเยี่ยมนี้เกี่ยวกับวิธีการ swizzling stackoverflow.com/questions/5339276/…โดยเฉพาะอย่างยิ่งในการดีบัก Method swizzling เป็นคุณสมบัติที่น่าทึ่ง แต่ควรใช้ IMHO เมื่อจำเป็นเท่านั้น ปัญหานี้แก้ไขได้ง่ายโดยใช้คำแนะนำที่ให้ไว้ที่นี่และจะช่วยให้ดูแลรักษาและอ่านโค้ดได้ง่ายขึ้นสำหรับโปรแกรมเมอร์คนอื่น ๆ
EsbenB

ฉันได้อ่านมาก่อนแล้วและฉันปฏิบัติตามหลักเกณฑ์ที่ระบุไว้ทั้งหมดในการนำไปใช้เพื่อความถูกต้อง นี่เป็นตัวอย่างที่ชัดเจนเมื่อวิธีการ swizzling ชนะ เป็นกรณีที่แยกได้ แต่เป็นเรื่องธรรมดาที่ไลบรารีพื้นฐานไม่สามารถใช้คุณลักษณะที่จำเป็นอย่างกว้างขวางในแพลตฟอร์มต่างๆได้อย่างง่ายดาย ตัวอย่างอื่น ๆ ที่แนะนำไม่ได้กำหนดช่องข้อความที่ไม่มีเคอร์เซอร์ตามความหมาย แต่จะดำเนินการผ่านการทำให้กรอบของเคอร์เซอร์สับสนหรือเป็นสี สำหรับโปรแกรมเมอร์มือใหม่อินเทอร์เฟซนี้อ่านง่ายที่สุดและทำตามที่คาดหวังไว้
Awesome-o
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.