用自定义视图替换字符串

时间:2017-02-08 10:00:58

标签: ios objective-c

我有一个带字符串的文本视图:

@"The best football player in the world is #OPTION, and the best basketball player is #OPTION?"

这是我必须做的一个例子。我想用下拉列表替换#OPTION,这意味着使用自定义视图。这取决于问题,它只能是一个#OPTION或更多。提前谢谢。

编辑: This is how it should look like

该字符串来自API,它出现在UITextView或UILabel

1 个答案:

答案 0 :(得分:3)

了解您的需求后,您面临的主要问题是不知道在哪里添加选择列表。

我为你的案例创建了两个类别,UILabel和UITextView, 以下这些帖子包含相关答案:

How do I locate the CGRect for a substring of text in a UILabel?

Get X and Y coordinates of a word in UITextView

这些类别可以找到内部字符串的CGRect,这是您应该选择拾取器的位置。

UILabel的下半部分是它没有很好地处理wordWrap换行模式,因此它找不到正确的位置,为了正确处理,你应该在需要时添加换行符以防万一你使用UILabel。

的UILabel:

extension UILabel
{
    func rectFor(string str : String, fromIndex: Int = 0) -> (CGRect, NSRange)?
    {
        // Find the range of the string
        guard self.text != nil else { return nil }

        let subStringToSearch : NSString = (self.text! as NSString).substring(from: fromIndex) as NSString

        var stringRange = subStringToSearch.range(of: str)

        if (stringRange.location != NSNotFound)
        {
            guard self.attributedText != nil else { return nil }

            // Add the starting point to the sub string
            stringRange.location += fromIndex

            let storage = NSTextStorage(attributedString: self.attributedText!)
            let layoutManager = NSLayoutManager()

            storage.addLayoutManager(layoutManager)

            let textContainer = NSTextContainer(size: self.frame.size)
            textContainer.lineFragmentPadding = 0
            textContainer.lineBreakMode = .byWordWrapping

            layoutManager.addTextContainer(textContainer)

            var glyphRange = NSRange()

            layoutManager.characterRange(forGlyphRange: stringRange, actualGlyphRange: &glyphRange)

            let resultRect = layoutManager.boundingRect(forGlyphRange: glyphRange, in:textContainer)

            return (resultRect, stringRange)
        }

        return nil
    }
}

用于无限搜索所有可用的子字符串(我建议在viewDidLayoutSubviews()中添加它以防你使用自动布局:

var lastFoundIndex : Int = 0

while let result = self.label.rectFor(string: "#OPTION", fromIndex: lastFoundIndex)
{
    let view : UIView = UIView(frame: result.0)
    view.backgroundColor = UIColor.red

    self.label.addSubview(view)

    lastFoundIndex = result.1.location + 1
}

与UITextView相同:

extension UITextView
{
    func rectFor(string str : String, fromIndex: Int = 0) -> (CGRect, NSRange)?
    {
        // Find the range of the string
        guard self.text != nil else { return nil }

        let subStringToSearch : NSString = (self.text! as NSString).substring(from: fromIndex) as NSString

        var stringRange = subStringToSearch.range(of: str)

        if (stringRange.location != NSNotFound)
        {
            guard self.attributedText != nil else { return nil }

            // Add the starting point to the sub string
            stringRange.location += fromIndex

            // Find first position
            let startPosition = self.position(from: self.beginningOfDocument, offset: stringRange.location)
            let endPosition = self.position(from: startPosition!, offset: stringRange.length)

            let resultRange = self.textRange(from: startPosition!, to: endPosition!)

            let resultRect = self.firstRect(for: resultRange!)

            return (resultRect, stringRange)
        }

        return nil
    }
}

用法:

var lastFoundTextIndex : Int = 0

while let result = self.textView.rectFor(string: "#OPTION", fromIndex: lastFoundTextIndex)
{
    let view : UIView = UIView(frame: result.0)
    view.backgroundColor = UIColor.red

    self.textView.addSubview(view)

    lastFoundTextIndex = result.1.location + 1
}

在您的情况下,textview提供最佳结果并使用包含的方法,示例代码使用标签&文本视图,在代码中初始化:

self.label.text = "The best football player in the world is\n#OPTION, and the best basketball player\n is #OPTION?"
self.textView.text = "The best football player in the world is #OPTION, and the best basketball player is #OPTION?"

输出只是在“#OPTION”字符串之上添加了视图:

希望这有帮助

编辑 - 添加了Objective-C变体:

创建2个扩展 - 1个用于UITextView,1个用于UILabel:

的UILabel:

@interface UILabel (UILabel_SubStringRect)

- (NSDictionary*)rectForString:(NSString*)string fromIndex:(int)index;

@end

#import "UILabel+SubStringRect.h"

@implementation UILabel (UILabel_SubStringRect)

- (NSDictionary*)rectForString:(NSString*)string fromIndex:(int)index
{
    if (string != nil)
    {
        NSString* subStringToSearch = [self.text substringFromIndex:index];
        NSRange stringRange = [subStringToSearch rangeOfString:string];

        if (stringRange.location != NSNotFound)
        {
            if (self.attributedText != nil)
            {
                stringRange.location += index;

                NSTextStorage* storage = [[NSTextStorage alloc] initWithAttributedString:self.attributedText];
                NSLayoutManager* layoutManager = [NSLayoutManager new];

                [storage addLayoutManager:layoutManager];

                NSTextContainer* textContainer = [[NSTextContainer alloc] initWithSize:self.frame.size];
                textContainer.lineFragmentPadding = 0;
                textContainer.lineBreakMode = NSLineBreakByWordWrapping;

                [layoutManager addTextContainer:textContainer];

                NSRange glyphRange;

                [layoutManager characterRangeForGlyphRange:stringRange actualGlyphRange:&glyphRange];

                CGRect resultRect = [layoutManager boundingRectForGlyphRange:glyphRange inTextContainer:textContainer];

                return @{ @"rect" : [NSValue valueWithCGRect:resultRect], @"range" : [NSValue valueWithRange:stringRange] };
            }
        }
    }

    return nil;
}

@end

的UITextView:

@interface UITextView (SubStringRect)

- (NSDictionary*)rectForString:(NSString*)string fromIndex:(int)index;

@end

#import "UITextView+SubStringRect.h"

@implementation UITextView (SubStringRect)

- (NSDictionary*)rectForString:(NSString*)string fromIndex:(int)index
{
    if (string != nil)
    {
        NSString* subStringToSearch = [self.text substringFromIndex:index];
        NSRange stringRange = [subStringToSearch rangeOfString:string];

        if (stringRange.location != NSNotFound)
        {
            if (self.attributedText != nil)
            {
                stringRange.location += index;

                UITextPosition* startPosition = [self positionFromPosition:self.beginningOfDocument offset:stringRange.location];
                UITextPosition* endPosition = [self positionFromPosition:startPosition offset:stringRange.length];

                UITextRange* resultRange = [self textRangeFromPosition:startPosition toPosition:endPosition];
                CGRect resultRect = [self firstRectForRange:resultRange];

                return @{ @"rect" : [NSValue valueWithCGRect:resultRect], @"range" : [NSValue valueWithRange:stringRange] };
            }
        }
    }

    return nil;
}

@end

使用示例 - UILabel:

int lastFoundIndex = 0;
NSDictionary* resultDict = nil;

do
{
    resultDict = [self.label rectForString:@"#OPTION" fromIndex:lastFoundIndex];

    if (resultDict != nil)
    {
        NSLog(@"result: %@", resultDict[@"rect"]);
        UIView* view = [[UIView alloc] initWithFrame:[resultDict[@"rect"] CGRectValue]];
        [view setBackgroundColor:[UIColor redColor]];

        [self.label addSubview:view];

        lastFoundIndex = (int)[resultDict[@"range"] rangeValue].location + 1;
    }
} while (resultDict != nil);

的UITextView:

int lastFoundTextIndex = 0;
NSDictionary* resultTextDict = nil;

do
{
    resultTextDict = [self.textview rectForString:@"#OPTION" fromIndex:lastFoundTextIndex];

    if (resultTextDict != nil)
    {
        NSLog(@"result: %@", resultTextDict[@"rect"]);
        UIView* view = [[UIView alloc] initWithFrame:[resultTextDict[@"rect"] CGRectValue]];
        [view setBackgroundColor:[UIColor redColor]];

        [self.textview addSubview:view];

        lastFoundTextIndex = (int)[resultTextDict[@"range"] rangeValue].location + 1;
    }
} while (resultTextDict != nil);