UITextView's text going beyond bounds

后端 未结 7 1469
深忆病人
深忆病人 2020-12-30 01:24

I have a non-scrollable UITextView with it\'s layoutManager maximumNumberOfLines set to 9, which works fine, but, I cannot seem to find a method in NSLayoutManager that rest

7条回答
  •  温柔的废话
    2020-12-30 02:25

    Here is a better answer I think. Whenever the shouldChangeTextInRange delegate method is called we call our doesFit:string:range function to see whether the resulting text height exceeds the view height. If it does we return NO to prevent the change from taking place.

    -(BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
        FLOG(@" called");
    
        // allow deletes
        if (text.length == 0)
            return YES;
    
        // Check if the text exceeds the size of the UITextView
        return [self doesFit:textView string:text range:range];
    
    }
    - (float)doesFit:(UITextView*)textView string:(NSString *)myString range:(NSRange) range;
    {
        // Get the textView frame
        float viewHeight = textView.frame.size.height;
        float width = textView.textContainer.size.width;
    
        NSMutableAttributedString *atrs = [[NSMutableAttributedString alloc] initWithAttributedString: textView.textStorage];
        [atrs replaceCharactersInRange:range withString:myString];
    
        NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:atrs];
        NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize: CGSizeMake(width, FLT_MAX)];
        NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
    
        [layoutManager addTextContainer:textContainer];
        [textStorage addLayoutManager:layoutManager];
        float textHeight = [layoutManager
                usedRectForTextContainer:textContainer].size.height;
        FLOG(@" viewHeight = %f", viewHeight);
        FLOG(@" textHeight = %f", textHeight);
    
        if (textHeight >= viewHeight - 1) {
            FLOG(@" textHeight >= viewHeight - 1");
            return NO;
        } else
            return YES;
    }
    

    EDIT OK you will also need to add some checks if you change the format of the text. In my case the user can change the font or make it bold, change paragraph style, etc.. So now any of these changes could also cause the text to exceed the textView borders.

    So first you need to make sure you are registering these changes with the textViews undoManager. See below for an example (I just copy the whole attributedString so I can put it back if undo is called).

    // This is in my UITextView subclass but could be anywhere
    
    // This gets called to undo any formatting changes 
    - (void)setMyAttributedString:(NSAttributedString*) atstr {
        self.attributedText = atstr;
        self.selectedRange = _undoSelection;
    }
    // Before we make any format changes save the attributed string with undoManager
    // Also save the current selection (maybe should save this with undoManager as well using a custom object containing selection and attributedString)
    - (void)formatText:(id)sender {
        //LOG(@"formatText: called");
        NSAttributedString *atstr = [[NSAttributedString alloc] initWithAttributedString:self.textStorage];
        [[self undoManager] registerUndoWithTarget:self
                                   selector:@selector(setMyAttributedString:)
                                     object:atstr];
        // Remember selection
        _undoSelection = self.selectedRange;
    
       // Add text formatting attributes
       ...
       // Now tell the delegate that something changed
       [self.delegate textViewDidChange:self];
    }
    

    Now check the size in the delegate and undo if it does not fit.

    -(void)textViewDidChange:(UITextView *)textView {
        FLOG(@" called");
        if ([self isTooBig:textView]) {
            FLOG(@" text is too big so undo it!");
            @try {
                [[textView undoManager] undo];
            }
            @catch (NSException *exception) {
                FLOG(@" exception undoing things %@", exception);
            }
        }
    }
    

提交回复
热议问题