iOS解析新浪微博的@##以及URL链接并展示

最近在做一个跟微博相关的应用。其中涉及到了对微博中@、##以及URL链接的解析与展示。分享一下个人处理的方式,希望对需要的人有所帮助。

最终的展现效果:

首先,第一步是你得从纯文本中找到它们。毫无疑问,采用正则表达式匹配是最佳的方式。我采用的是RegexKitLite库。

解析这三种格式的正则表达式如下:

/*****************************regular expressions**************************/
#define ALABEL_EXPRESSION @"(<[aA].*?>.+?</[aA]>)"
#define HREF_PROPERTY_IN_ALABEL_EXPRESSION @"(href\\s*=\\s*(?:\"([^\"]*)\"|\'([^\']*)\'|([^\"\'>\\s]+)))"
#define URL_EXPRESSION @"([hH][tT][tT][pP][sS]?:\\/\\/[^ ,'\">\\]\\)]*[^\\. ,'\">\\]\\)])"
#define AT_IN_WEIBO_EXPRESSION @"(@[\u4e00-\u9fa5a-zA-Z0-9_-]{4,30})"
#define TOPIC_IN_WEIBO_EXPRESSION @"(#[^#]+#)"

分别为:匹配<a></a>标签,匹配a标签的href属性,匹配URL地址,匹配微博中的@,匹配微博中的##(topic);

对于文本的处理:

- (NSString*)handleForShowing{
    NSArray *expressions = expressions = [[NSArray alloc] initWithObjects:
                                          AT_IN_WEIBO_EXPRESSION,
                                          TOPIC_IN_WEIBO_EXPRESSION,
                                          URL_EXPRESSION,
                                          nil];

    //如果有<a></a>则先进行预处理
    NSString *aLabelExpression=@"(<[aA].*?>.+?</[aA]>)";
    if ([self stringByMatching:aLabelExpression]) {
        NSArray *matchedArr=[self componentsMatchedByRegex:ALABEL_EXPRESSION];
        for (NSString *matchedItem in matchedArr) {
            NSString *tmpHrefVal=[[matchedItem stringByMatching:HREF_PROPERTY_IN_ALABEL_EXPRESSION]
								stringByMatching:URL_EXPRESSION];
            if (tmpHrefVal) {
                self=[self replaceAll:matchedItem with:tmpHrefVal];
            }
        }
    }    

    for (NSString *expression in expressions)
	{
        NSString *replaceStr=@"";
        if ([expression contains:@"@"]) {
            replaceStr=@"<a href=\"$1\">$1</a>";
        }else if([expression contains:@"#"]){
            replaceStr=@"<a href=\"$1\">$1</a>";
        }else{
            replaceStr=@"<a href=\"$1\">$1</a>";
        }
        self=[self stringByReplacingOccurrencesOfRegex:expression withString:replaceStr];
	}
    [expressions release];
    return self;
}

这里需要注意的是,微博的种类有很多种。大部分的地址都直接是纯粹的Url,但其中的一种微博(记不清是视频还是音乐的)返回的url是附带在a标签的内部作为href属性的。这样就不便于统一处理,所以我采取的做法是:首先,让解析流程统一化。也就是先把文本中包含的a标签去掉,把href包含的链接作为纯文本。然后,解析出微博中的这三种特殊字符串,并为其包裹一层a标签。

接着,谈谈关于展示的问题。上面你可能想知道为什么需要包上一层a标签呢?那是为了展示用的。

如何让@、##、URL高亮呢,我目前只找到三种展示它的方式:

(1) Three
20中的TTStyledTextLabel

(2) 原先用于展示Twitter,后来被改写支持中文展示的FancyLabel

(3) 最擅长呈现html标记的UIWebView

三种我全部试过,最后还是选择了UIWebView。下面说明一下未曾使用前两种的原因。

其实,原本我是不倾向于使用UIWebView,我想能使用普通的控件,就无需把UIWebView这种大部头搬出来“救场”了(据说UIWebView的内存泄露问题由来已久,后续我会谈到这个问题,本篇不作深究)。

我首先尝试的是第二种:FancyLabel。开始使用的时候,觉得好像真能展示。它文件内部已经存在了解析的正则表达式了,并且RegexKitLite也是作为它的组件使用的(可见原理都是一样的)。但展示了几个发现:@、##、URL各种不同方式的复杂搭配,它显得有些无能为力(这其实是它附带的正则表达式匹配得不够健全的问题),但当时我却不是这个原因放弃它的。放弃的原因是,它无法“折断换行”,也就是,当一个匹配项它呈现的位置已经在一行的末尾了,它无法呈现匹配项的一部分,同时将另一部分折断到下一行的起始去显示,它的处理方式时另起一行。这看起来非常难看,后面还有空间空出来了,就直接跑到下面一行去展示了,并且该行它也是独占的,后面的文本也不得不另起一行,显得非常不流畅。最致命的是:你无法算准它的高度,因为它归根到底是一个UILabel的子类。对于Label的高度,在它的宽度固定的情况下,通常都是带着它文本的字体大小算出来的。但这个时候,你已经无法准确地计算高度了(因为普通的计算方式,它默认Label文本的呈现方式是那种“流式”的,你换行起始占用了增大了它的高度,但在算的时候你无法将这些情况估算到),所以它影响了接下来用于呈现评论/转发等控件的布局。

放弃了第二种,又在网上寻找其他的解决方案,发现大名鼎鼎的Three
20里面,有一种呈现富客户端文本的控件:TTStyledTextLabel,支持对连接、简单html标签以及样式。那我将这些解析出来的内容,包裹上<a></a>不就可以了吗?我当时就是这么想。结果同样不是太理想,也是无法折断换行的原因。当然,如果你下面没有依赖它来布局的控件(如同你在web中使用的是绝对定位,而不是相对定位一样)。那么你还是可以使用它的。

这可不像FancyLabel,你直接把接受到的纯文本丢给它一了百了。它自身只负责普通html标签以及链接的解析,所以你给它的文本必须是处理好之后的。其实,你处理好之后展现也是没有问题的。如果你使用的是UITableView的方式来展示它,并且你自定义了UITableViewCell来呈现它,会显得很麻烦。因为这个部分可能要计算两次高度:在heightForRowAtIndexPath代理方法中算一次,在自定义的Cell内部,为了下面控件的布局,必须算一次。其实,TTStyledTextLabel自身是可以返回高度的,并且它返回的高度是正确的(即使它有些匹配项是另起一行的,但占用的“额外”高度也被它包含在内,这也是我认为它很强大的地方)。但,在heightForRowAtIndexPath计算起来就不那么简单了,我简单得把一样的文本给一个“帮助方法”,它内部构建一个TTStyledTextLabel对象,获取到文本,并算高度,还是有所偏差。所以说能不能使用,主要是看你用怎样的方式来展示你的微博内容。如果你想用,这样是不够的,因为它只是完成了呈现的工作。使用过新浪微博或者腾讯微博客户端的人都知道,@、##、URL这些高亮文本是可以点击的。很遗憾的是,TTStyledTextLabel自身对于a标签的点击事件仅仅只是,用它内部的另一个浏览器组件来加载href属性的URL,这显然不是我们想要的。为了改变它这种默认行为,我继承了TTStyledTextLabel,重写了它的点击事件,以拦截它的默认行为:

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
    TTTableView* tableView = (TTTableView*)[self isKindOfClass:[TTTableView class]];
    if (!tableView) {
        if (self.highlightedNode) {
            // nodes to converse with.
            if ([self.highlightedNode isKindOfClass:[TTStyledLinkNode class]]) {
                //NSLog([(TTStyledLinkNode*)_highlightedNode URL]);
            } else if ([self.highlightedNode isKindOfClass:[TTStyledButtonNode class]]) {
                //NSLog([(TTStyledButtonNode*)_highlightedNode URL]);
            } else {
                NSLog(@"others");
            }
            self.highlightedNode=nil;
        }
    }
}

在上段代码中:NSLog(@”others”);部分,你可以去实现你的逻辑:比如点击@XXX,弹出XXX的个人详情。你可以在地址中包含你需要的数据,在上面可以通过获得url来得到你的数据。

你可能会好奇,为何这两种方式都出现这种无法折断换行的行为呢。这也是由它们的实现方式决定的。你看到上面这段代码中,比如:TTStyledLinkNode、TTStyledButtonNode,它把相应的匹配项都转化为特定的Node,对这段Node单独绘制(这里牵扯到CoreText以及NSAttributeString等,具体未有空详细研究),比如某个子节点是可点击的,那可能就是TTStyledButtonNode类型,也就形如一个Button。很明显,一个Button内的文本,如果在一个区域显示不下,只能另起一行了。

要应对这种方式,看来不得不请出:UIWebView。它本身也擅长于图文混排以及富文本的呈现。你只要按照上面的方式处理好文本,然后在UIWebView里设置相关样式,就可以完美呈现,甚至图片都省去了获取并处理的过程。形如:

一不用二不休,下面的转发与评论的列表,也顺便用它来展示吧。

展示的问题完美地解决了,下面还要能够响应点击事件。这里同样要改变UIWebView中a标签的默认行为,使其响应本地调用(obj-c代码)。怎么办呢?用js给a标签注册一个click
event,然后它调用一个方法,发起一个请求:

sendCommand: function (cmd,param){
		var url="FEB:"+cmd+":"+param;
		document.location = url;
}

它其实并不是一个真实意义上的url地址,只是一个携带了操作命令以及参数的“virtual url”。发起的任何请求都会被:

UIWebView 的shouldStartLoadWithRequest代理方法截获。

然后在这里,你可以判断相关的请求行为,获取参数,进行你的本地处理,比如弹出XXX的详情的模式窗口:

- (BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request
			navigationType:(UIWebViewNavigationType)navigationType {
    NSString *requestString = [[request URL] absoluteString];
    NSArray *components = [requestString componentsSeparatedByString:@":"];
    if ([components count] > 1 &&
	[(NSString *)[components objectAtIndex:0] isEqualToString:[@"FEB" lowercaseString]]) {
        NSString *cmdName=(NSString *)[components objectAtIndex:1];
        if([cmdName isEqualToString:@"loadRepublishList"]) {

		} else if([cmdName isEqualToString:@"loadCommentList"]){

		}

	}
}

这篇就分享到这里,下面准备就UIWebView的使用分享一些经验。比如使用一些模板引擎来增强代码的可读性以及提升开发效率。对于“微博详情界面”我使用了两个模板:一个是用于呈现微博本身的HTML模板引擎;另一个是JS模板引擎(为了提升响应速度,并且为了配合转发/评论列表的异步加载)。

最后,为自己打个广告:

这是本人的第一个“个人iOS应用”,它是一款能够帮助你同时在多个社交平台和好友沟通交流以及分享的应用。支持一键发表状态/心情/照片到所有绑定平台,快速浏览/回复/分享,以及多终端同时在线浏览好友微博与好友互动,为你提供更为便捷的社交体验。

开发这款应用的主要目的是:熟悉整个iOS的开放、上架流程,以及开发技术,为转型iOS开发做准备。它使用了大概十几个开源库,之后会跟大家分享~

目前这个应用已经在“人人应用广场”、“新浪微博应用广场”上线了(腾讯微博还在准备中),如果希望我分享更多的内容,请到App
Store下载它并给它一个评价。我会持续不断地对它进行更新,以让它看起来更加专业。

另外欢迎交流:yanghua1127@gmail.com

原文发布时间为:2012-11-06

本文作者:vinoYang

本文来自合作伙伴CSDN博客,了解相关信息可以关注CSDN博客。

时间: 2024-10-22 07:16:53

iOS解析新浪微博的@##以及URL链接并展示的相关文章

Flash内加URL链接的一点经验

链接     制作一些含有URL链接的Flash广告条,将他们放到页面中,会遇到一些奇怪的问题,比如打开链接的时候会被google工具条屏蔽,鼠标放到链接上光标形状会在手型和鼠标之间不停的闪,最后解决了这些小问题,总结一下经验,希望对遇到类似问题的朋友们有用.   使用onRelease事件而不使用onPress事件,使用onPress事件,打开链接的时候出现过被google工具条拦截的现象,另外web上的链接也都是onRelease事件,即鼠标松开的时候才打开链接,符合用户点击链接的习惯.  

JS获取url链接字符串 location.href

 有时候我们需要获取当前网页的网址,方便我们判断,一般情况我们都是通过location.href来获取 js获取url链接字符串:location.href    可以对其进行截取,从而获取传送的参数,常用如下:    location.href.indexOf("?")------获取?的index值.    注意:这里的location.href可不是指的现在地址栏里的地址,而是页面实际的地址.    另外,一些题外话:    C#中获取字符所在位置的索引,也是用IndexOf来获

重载-急求 iframe 中url 链接是二次跳转,跳转后怎么让其还在iframe?

问题描述 急求 iframe 中url 链接是二次跳转,跳转后怎么让其还在iframe? 我在js中动态设置Iframe src的值,但是src 中的url 有时候是二次跳转链接,(比如微信 公众号的url,需要先进行授权后在跳入目标页面),当跳到第二次的链接后, 页面已经跳出了这个iframe, 求大神回答: 如何让第二次跳转的链接仍然显示在 这个iframe中. 我动态设置iframe 值得代码: <script> var shareurl = $("#modify_panel

新浪微博审核问题 URL处请如何填写

问题描述 新浪微博审核问题 URL处请如何填写 您的应用"移动乐生活"未能通过我方的来源文案审核,其原因是:"URL处请填写与应用名称一致的客户端专用下载页面或在软件商店页面地址,网盘,论坛页面无效,请在应用截图中体现此应用与新浪微博有关功能".请您依据我们的建议和<微博开放平台审核指南>,再次修改后重新提交审核申请.查看常见驳回理由说明及解决方案 http://t.cn/zluaxfa.感谢您对新浪微博开放平台的支持! 他不给开放,市场给拒绝,市场拒绝

站内优化:url链接标准化

很多新手朋友甚至有一定经验的老手SEO都会遇到链接标准化的问题,站内优化:url链接标准化,因为小站影响不了多大,除了发链接,发文章也就没什么事好做了,但对于大型网站或追求极致的朋友,url链接标准化注意以下几个要点.企业qq多少钱网seoer分析如下: 1.目录是带应该带"/"结尾,首页.目录页往往有些链接会带上斜杠,对于该种类型的链接标准法,见人见智,有的SEOER认为不带/,地址比较短有 利于优化,本人认为,带不带/不会直接影响网站优化,如果带/,那么在做链接的时候也全部带是/结

javascript-Python 爬虫如何获取onclick(非url链接)之后网页?

问题描述 Python 爬虫如何获取onclick(非url链接)之后网页? Python 爬虫如何获取onclick里面内容,不需要用selenium 模拟点击,而是直接获得哦你click返回参数?具体比如说https://www.tripadvisor.com/ShowUserReviews-g57592-d416577-r357988112-The_Ivy_Inn_Restaurant-Charlottesville_Virginia.html#REVIEWS Tripadvisor 网站

spring-如何解析如下格式的URL

问题描述 如何解析如下格式的URL 现有URL如下: http://localhost:8080/sheetTest/base/updateData.do?sheet=7&edit=true&rows[0]=2&cols[0]=4&values[0]=11&styles[0]=bold%3Afalse%3Bitalic%3Afalse%3Bcolor%3A000000%3Bbgcolor%3Affffff%3Balign%3Aleft 请问如何在Action中获取参

JSP对URL链接中的中文乱码处理方法总结_JSP编程

IE缺省对URL后面的参数是不编码发送的,但是Tomat缺省是按ISO8859-1来进行URL编码的,因此才会出错. 方法一: 对URL链接进行二次编码: <a onclick="javascript:window.open(encodeURI(encodeURI('./DispatchAction.do?efFormEname=FKRY0001&code_type=中文参数')))">测试</a> 或者单独对参数进行二次编码: var code_typ

php解析字符串里所有URL地址的方法_php技巧

本文实例讲述了php解析字符串里所有URL地址的方法.分享给大家供大家参考.具体如下: <?php // $html = the html on the page // $current_url = the full url that the html came from //(only needed for $repath) // $repath = converts ../ and / and // urls to full valid urls function pageLinks($ht