当前位置:  编程技术>移动开发
本页文章导读:
    ▪用Core Text创设简单杂志应用(2)        用Core Text创建简单杂志应用(2)现在,我们已经获得所有的文本块和格式标签(如同前面见到的<font>标签)。需要做的仅仅是遍历文本块数组然后构建NSAttributedString .在方法体中加入以.........
    ▪ wamp开发环境下安装Yii 可能会遇到的有关问题及解决办法        wamp开发环境下安装Yii 可能会遇到的问题及解决方法1:yiic执行出现不是内部或外部命令的解决办法 右击我的电脑-->属性-->高级-->环境变量-->系统变量 设置为"D:\wamp\bin\php\php5.2.5;D:\wam.........
    ▪ 用Core Text创办简单杂志应用(3)       用Core Text创建简单杂志应用(3)有栏、有可以样式化的文字,但没有图片。用Core Text来绘制图片不太容易——它毕竟只是一个文本绘制框架。幸运的是我们已经有一个简单的标签解析器。我.........

[1]用Core Text创设简单杂志应用(2)
    来源: 互联网  发布时间: 2014-02-18
用Core Text创建简单杂志应用(2)

现在,我们已经获得所有的文本块和格式标签(如同前面见到的<font>标签)。需要做的仅仅是遍历文本块数组然后构建NSAttributedString .

在方法体中加入以下代码:

for (NSTextCheckingResult* b in chunks) {
   NSArray* parts = [[markup substringWithRange:b.range]
                      componentsSeparatedByString:@"<"]; //1
   CTFontRef fontRef = CTFontCreateWithName(
                 (CFStringRef)self.font, 
                 24.0f, NULL);   //apply the current text style //2 
   NSDictionary* attrs=[NSDictionary dictionaryWithObjectsAndKeys:
       (id)self.color.CGColor, kCTForegroundColorAttributeName,
       (id)fontRef, kCTFontAttributeName,   
       (id)self.strokeColor.CGColor,kCTStrokeColorAttributeName,
       (id)[NSNumber numberWithFloat: self.strokeWidth], 
       (NSString *)kCTStrokeWidthAttributeName, nil];
   [aString appendAttributedString:[[[NSAttributedString alloc] 
        initWithString:[parts objectAtIndex:0] attributes:attrs]
        autorelease]];  
   CFRelease(fontRef);       //handle new formatting tag //3
   if ([parts count]>1) {
       NSString* tag = (NSString*)[parts objectAtIndex:1];     
       if ([tag hasPrefix:@"font"]) {
           //stroke color 
           NSRegularExpression* scolorRegex = [[[NSRegularExpression
               alloc] initWithPattern:@"(?<=strokeColor=\")\\w+" 
               options:0 error:NULL] autorelease]; 
           [scolorRegex enumerateMatchesInString:tag options:0 
               range:NSMakeRange(0, [tag length])
               usingBlock:^(NSTextCheckingResult *match,
               NSMatchingFlags flags, BOOL *stop){  
               if ([[tag substringWithRange:match.range] 
isEqualToString:@"none"]) { 
                    self.strokeWidth = 0.0;
               } else { 
                    self.strokeWidth = -3.0;  
                    SEL colorSel = NSSelectorFromString([NSString stringWithFormat: @"%@Color", [tag substringWithRange:match.range]]);                        
                    self.strokeColor = [UIColor
                          performSelector:colorSel];                 
               }
           }];   
           //color 
           NSRegularExpression* colorRegex = [[[NSRegularExpression
               alloc] initWithPattern:@"(?<=color=\")\\w+"
               options:0 error:NULL] autorelease];
           [colorRegex enumerateMatchesInString:tag options:0 
               range:NSMakeRange(0, [tag length])
               usingBlock:^(NSTextCheckingResult *match,
               NSMatchingFlags flags, BOOL *stop){  
                    SEL colorSel = NSSelectorFromString([NSString 
stringWithFormat: @"%@Color", [tag substringWithRange:match.range]]);                             
          self.color = [UIColor 
               performSelector:colorSel];   
          }];
          //face    
          NSRegularExpression* faceRegex = [[[NSRegularExpression
               alloc] initWithPattern:@"(?<=face=\")[^\"]+" 
               options:0 error:NULL] autorelease];
          [faceRegex enumerateMatchesInString:tag options:0
               range:NSMakeRange(0, [tag length])
               usingBlock:^(NSTextCheckingResult *match,
          NSMatchingFlags flags, BOOL *stop){    
               self.font = [tag substringWithRange:match.range];
          }];
       } //end of font parsing
    } }
return (NSAttributedString*)aString;

代码稍有点多,但不要担心,我会逐句进行讲解。

  • 在对文本块进行遍历的过程中,我们把文本块根据“<”符号分为前后两段。前半段parts[0]作为文本内容,后半段作为标签内容用于格式化后面的文本。
  • 接下来,创建一个dictionary用于存放格式化属性——通过这种方式我们可以将格式属性传递给NSAttributedString。注意NSAttributedString中key的名称——苹果将它们定义成望名生意的常量(详见 Core Text String Attributes Reference)。appendAttributedString: 方法调用则将新的文本块和格式属性添加到最终的NSAttributedString。
  • 最后,检查在文本块后面是否有标签出现,如果有并且是font开头,开始对每种可能的标签属性进行解析。对于face属性,将字体名赋给self.font即可;对于color,我们需要通过正则式colorRegex从<font color="red">中取出单词red,然后创建一个名为redColor的选择器,然后在UIColor上进行调用(这将返回UIColor的一个红色实例)。当然,当这个选择器不存在时,程序将崩溃。但是在本示例中,这段代码能满足我们的需要。strokeColor属性与color属性一样,但当strokeColor值为none时,我们将strokewidth也设置为0,这样将取消删除线样式。
  • 注: 关于代码中正则式的具体含义,可以理解为“查找color="之后的字符串(由普通字符数字、字母和下划线组成,不包括标点符号),一直到右尖括号”。更多请参考苹果的“NSRegularExpression类参考”。

    译者注:正则式“(?<=color=\")\\w+”可以分为两部分,第一部分“(?<=color=\")”使用了零宽度正向回查(非捕获组)模板“(?<=pattern)”,这部分匹配结果不捕获,第二部分“\\w+”匹配1或多个普通字符(数字、字母和下划线),这部分匹配结果会被捕获。

    嗨,我们已经完成了一半的工作了!现在attrStringFromMarkup:方法会对markup进行分割,形成NSAttributedString,并准备传递给CoreText。

    打开CTView.m,在 @implementation前面加入:

    #import "MarkupParser.h"

    将定义attString对象的语句替换为:

     the line whereattString is defined - replace it with the following code:

    MarkupParser* p = [[[MarkupParser alloc] init] autorelease];  NSAttributedString* attString = [p attrStringFromMarkup: @"Hello <font color=\"red\">core text <font color=\"blue\">world!"];

    这里,我们创建了一个新的解析器对象,并传入一个使用了标记语法的字符串给attrStringFromMarkup:方法,已得到一个NSAttributeString字符串。

    运行程序。

    很酷吧?仅仅50行的代码,没有计算文本位置、没有硬编码文本样式,仅仅用一个简单文本文件就可以编辑出杂志的内容!

    只要你愿意,这个简单解析器可以无限制地扩展下去。

    杂志的基本布局

    我们可以显示文本,这是一个好的开端。但作为杂志而言,我们希望能够分栏显示——使用Core Text很容易做到这点。

    在这样做之前,首先让我们将长文本加载进应用程序,以便我们有足够的文本显示成多行。

    点击File\New\New File,选择iOS\Other\Empty,点击 Next。将文件命名为 test.txt, 然后点击Save。

    然后编辑文件内容,如 这个文件所示。

    打开CTView.m ,找到创建 MarkupParser 和 NSAttributedString 的两句代码,删除它们。我们将在drawRect:方法之外加载text文件,这个功能不应该由drawRect:方法来实现。将attString修改为实例变量和类的属性。

    打开 CoreTextMagazineViewController.m,删除所有内容,加入以下内容:

    #import "CoreTextMagazineViewController.h" 
    #import "CTView.h" 
    #import "MarkupParser.h"   
    @implementation CoreTextMagazineViewController   
    - (void)viewDidLoad {
         [super viewDidLoad]; 
         NSString *path = [[NSBundle mainBundle] pathForResource:@"test"
              ofType:@"txt"];
         NSString* text = [NSString stringWithContentsOfFile:path 
              encoding:NSUTF8StringEncoding error:NULL]; 
         MarkupParser* p = [[[MarkupParser alloc] init] autorelease];
         NSAttributedString* attString = [p attrStringFromMarkup: text];
         [(CTView*)self.view setAttString: attString]; 
    }   
    @end

    当应用程序的视图一被加载,会读取test.txt文件的内容,将其转换为NSAttributedString,然后设置到CTView的attString属性。当然,我们需要为CTView增加相应的属性。

    在CTView.h中定义 3 个实例变量:

    float frameXOffset; 
    float frameYOffset;   
    NSAttributedString* attString;

    在CTView.h 和 CTView.m 中定义 attString属性:

    //CTView.h 
    @property (retain, nonatomic) NSAttributedString* attString;   
    //CTView.m 
    //just below @implementation ... 
    @synthesize attString;   
    //at the bottom of the file 
    -(void)dealloc {
         self.attString = nil;
         [super dealloc]; 
    }

    运行程序,文本文件的内容被显示到了屏幕上。

    如何将这些文本分栏?CoreText提供了一个便利函数 - CTFrameGetVisibleStringRange。该函数能够告诉你在一个固定的框内能够放下多少文字。基本思路是——创建栏,判断它适合放入多少文字,如果放不下——创建新的栏,以此类推(这里的“栏”就是一个CTFrame实例,栏其实就是一个矩形框)。

    首先我们应当有栏,然后是页,然后是整本杂志,因此我们让CTView的继承了UIScrollView,以便获得分页和滚动的能力。

    打开CTView.h ,将 @interface 一行改为:

    @interface CTView : UIScrollView<UIScrollViewDelegate> {

    现在,CTView已经继承了UIScrollView。我们要让它能够分页。

    我们已经在drawRect:方法中创建了framesetter和frame。当存在分栏且样式各不同的情况下,更好的方法是让这个动作只需进行一次。因此我们将创建一个新的类CTColumnView,它仅仅负责渲染指定的CoreText文本,在我们的CTView类中,我们只需创建一次CTColumnView并将它们加到subViews中。

    简单而言:CTView负责滚动、分页和创建栏;而CTColumnView将实际上负责将文本内容渲染在屏幕上。

    点击File\New\New File, 选择 iOS\Cocoa Touch\Objective-C class, 然后点击Next。选择 UIView 作为父类,点击 Next, 将新类命名为 CTColumnView.m, 然后点击Save. 这是CTColumnView类代码:

    //inside CTColumnView.h   
    #import <UIKit/UIKit.h> 
    #import <CoreText/CoreText.h>   
    @interface CTColumnView : UIView { 
        id ctFrame; 
    }   
    -(void)setCTFrame:(id)f; 
    @end   
    //inside CTColumnView.m 
    #import "CTColumnView.h"   
    @implementation CTColumnView 
    -(void)setCTFrame: (id) f {
         ctFrame = f; 
    }   
    -(void)drawRect:(CGRect)rect {
         CGContextRef context = UIGraphicsGetCurrentContext(); 
         // Flip the coordinate system 
         CGContextSetTextMatrix(context, CGAffineTransformIdentity);  
         CGContextTranslateCTM(context, 0, self.bounds.size.height);
         CGContextScaleCTM(context, 1.0, -1.0);
         CTFrameDraw((CTFrameRef)ctFrame, context); 
    } 
    @end

    这个类仅仅负责渲染一个CTFrame。在这本杂志中,我们应当为每个栏单独创建一个实例。

    首先,要在CTView中加入一个属性,用于保存所有CTFrame,同时声明一个buildFrames方法,用于创建所有栏:

    //CTView.h - at the top 
    #import "CTColumnView.h"   
    //CTView.h - as an ivar 
    NSMutableArray* frames;   
    //CTView.h - declare property 
    @property (retain, nonatomic) NSMutableArray* frames;   
    //CTView.h - in method declarations 
    - (void)buildFrames;   
    //CTView.m - just below 
    @implementation @synthesize frames;   
    //CTView.m - inside dealloc 
    self.frames = nil;

    在buildFrames 方法中创建CTFrame并将它们添加到frames数组。

    - (void)buildFrames {
         frameXOffset = 20; //1
         frameYOffset = 20;
         self.pagingEnabled = YES;
         self.delegate = self;
         self.frames = [NSMutableArray array];
         CGMutablePathRef path = CGPathCreateMutable(); //2 
         CGRect textFrame = CGRectInset(self.bounds, frameXOffset, frameYOffset);
         CGPathAddRect(path, NULL, textFrame );
         CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attString); 
         int textPos = 0; //3
         int columnIndex = 0; 
         while (textPos < [attString length]) { //4 
            CGPoint colOffset = CGPointMake( (columnIndex+1)*frameXOffset + columnIndex*(textFrame.size.width/2), 20 );
            CGRect colRect = CGRectMake(0, 0 , textFrame.size.width/2-10, textFrame.size.height-40);  
            CGMutablePathRef path = CGPathCreateMutable();
            CGPathAddRect(path, NULL, colRect);  
            //use the column path 
            CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(textPos, 0), path, NULL); 
            CFRange frameRange=CTFrameGetVisibleStringRange(frame); //5     
            //create an empty column view 
            CTColumnView* content = [[[CTColumnView alloc] initWithFrame: CGRectMake(0, 0, self.contentSize.width, self.contentSize.height)] autorelease];
            content.backgroundColor = [UIColor clearColor]; 
            content.frame = CGRectMake(colOffset.x, colOffset.y, colRect.size.width, colRect.size.height) ; 
            //set the column view contents and add it as subview 
            [content setCTFrame:(id)frame];  //6 
            [self.frames addObject: (id)frame];   
            [self addSubview: content];   
            //prepare for next frame 
            textPos += frameRange.length; 
            //CFRelease(frame);     
            CFRelease(path); 
            columnIndex++;
         }  
         //set the total width of the scroll view 
         int totalPages = (columnIndex+1) / 2; //7
         self.contentSize = CGSizeMake(totalPages*self.bounds.size.width, textFrame.size.height); 
    }

    先来看看代码。

  • 进行初始设置——定义了x和y偏移,开启分页模式,创建frames数组。
  • 用视图的bounds创建路径和frame(利用x和y偏移在四边留白)。
  • 声明textPos变量,用于存放当前文本位置。声明columnIndex,用于记录当前已经创建的栏数。
  • while 循环,直到整个文本结束。在循环内部,我们创建了栏的矩形边界:colRect是一个CGRect,根据栏号(columnIndex)计算各栏的origin和size。注意,我们创建的栏是从左至右排列的(不是从上到下)。
  • 此句使用CTFrameGetVisibleStringRange函数计算本栏的文本内容(本例中是一个文本栏)。textPos根据range的长度自动累加,以便下次循环用于下一栏(假设还有更多的字符)。
  • 这次,我们不使用以前的方法绘制CTFrame。而是把它作为参数传递给新创建的CTColumnView,将它保存到self.frames数组以备后用,然后把CTColumnView添加到scrollView的subviews中。
  • 最后,totalPages中保存所有生成的页的数目。 更新CTView的contentSize属性,以便内容超过1页时滚动scrollview。
  • 现在,当Coret Text准备妥当后调用buildFrames方法。在CoreTextMagazineViewController.m的viewDidLoad:方法最后加入代码:

    [(CTView *)[self view] buildFrames];

    在此之前,将CTView.m的drawRect:方法删除。现在,我们通过CTColumnView来渲染文字,因此不需要在CTView的drawRect:方法中做任何额外的工作。

    点击Run,iPad屏幕显示如下图所示!左右滑动以进行翻页……简直是酷极了!






        
    [2] wamp开发环境下安装Yii 可能会遇到的有关问题及解决办法
        来源: 互联网  发布时间: 2014-02-18
    wamp开发环境下安装Yii 可能会遇到的问题及解决方法
    1:yiic执行出现不是内部或外部命令的解决办法
    右击我的电脑-->属性-->高级-->环境变量-->系统变量 设置为"D:\wamp\bin\php\php5.2.5;D:\wamp\www\yii\framework"(具体看自己的环境配置情况)
    注:添加环境变量后,关闭cmd重新打开!
    2:Yii 自带的yiic.bat 找不到php.exe.
    解决方法:
    yii/framework/yiic.bat,
    修改if "%PHP_COMMAND%" == "" set PHP_COMMAND=php.exe
    为 if "%PHP_COMMAND%" == "" set PHP_COMMAND=D:\wamp\php\php.exe(后面php.exe的路径根据自己的安装环境而定)
    
    
    

    在cmd.exe窗口中执行:

    创建一个网站(yiicms) yiic webapp d:/wamp/www/yiicms 

    创建一个模块(admin)

    yiic shell d:\wamp\www\yiicms\index.php

    >> module admin

    >> exit 
    退出shell
    配置数据库和模块
    先在配置文件main.php中设置连接mysql
    return array(
    

       ..... 

        // 模块设置
        'modules'=>array( //必须在 components 配置上面
            'admin'=>array(),
        ),
        // application components
        'components'=>array(
            ......
            'db'=>array(
                'connectionString' => 'sqlite:protected/data/testdrive.db',
            ),        
            // uncomment the following to use a MySQL database
            'db'=>array(
                'connectionString' => 'mysql:host=localhost;dbname=yiicms',
                'emulatePrepare' => true,
                'username' => 'root',
                'password' => '',
                'charset' => 'utf8',
                'tablePrefix' => 't_',  // 指定表前缀
            ),
            ......
        ),
        ......
    );
    创建模型(Category.php)
    yiic shell d:\wamp\www\yiicms\index.php
    >> model Category
    通过crud在模块admin下创建与Category.php模型对应的CategoryController.php和视图
    >> crud Category admin/category
    >> exit

        
    [3] 用Core Text创办简单杂志应用(3)
        来源: 互联网  发布时间: 2014-02-18
    用Core Text创建简单杂志应用(3)

    有栏、有可以样式化的文字,但没有图片。用Core Text来绘制图片不太容易——它毕竟只是一个文本绘制框架。

    幸运的是我们已经有一个简单的标签解析器。我们可以容易地从字符串中取出图片。

    在 Core Text中绘制图片

    本质上,Core Text是不能绘制图形的。但是,它同时还是一个布局引擎,因此我们可以让它流出一些空白以便我们有绘图的空间。在drawRect:方法中绘图是一件简单的事。

    首先看我们如何在文本绘制时保留足够的空白空间。还记得文本块其实都是一些CTRun实例吗?简单地为某个CTRun指定一个委托,然后让委托对象告诉Core Text要该 CTRun 上升/下降多少空间,以及空间的宽度。如下图所示:


    当Core Text“到达”某个被设置了委托的CTRun时,它会询问委托“要预留给这个CTRun多少宽度和高度?”。这样,就可以在文本中挖出一个洞——用于绘制图片。

    让我们在我们的标签解析器中增加对<img>的支持。打开MarkupParser.m找到"} //end of font parsing"一行,在后面加入一下代码:

    if ([tag hasPrefix:@"img"]) {

     

        __block NSNumber* width = [NSNumbernumberWithInt:0];

        __block NSNumber* height = [NSNumbernumberWithInt:0];

        __block NSString* fileName = @"";

     

        //width

        NSRegularExpression* widthRegex =[[[NSRegularExpression alloc]initWithPattern:@"(?<=width=\")[^\"]+" options:0error:NULL] autorelease];

        [widthRegex enumerateMatchesInString:tagoptions:0 range:NSMakeRange(0, [tag length]) usingBlock:^(NSTextCheckingResult*match, NSMatchingFlags flags, BOOL *stop){

            width = [NSNumbernumberWithInt: [[tag substringWithRange: match.range] intValue] ];

        }];

     

        //height

        NSRegularExpression* faceRegex =[[[NSRegularExpression alloc]initWithPattern:@"(?<=height=\")[^\"]+" options:0error:NULL] autorelease];

        [faceRegex enumerateMatchesInString:tagoptions:0 range:NSMakeRange(0, [tag length]) usingBlock:^(NSTextCheckingResult*match, NSMatchingFlags flags, BOOL *stop){

            height = [NSNumbernumberWithInt: [[tag substringWithRange:match.range] intValue]];

        }];

     

        //image

        NSRegularExpression* srcRegex =[[[NSRegularExpression alloc]initWithPattern:@"(?<=src=/index.html")[^\"]+" options:0error:NULL] autorelease];

        [srcRegex enumerateMatchesInString:tag options:0range:NSMakeRange(0, [tag length]) usingBlock:^(NSTextCheckingResult *match,NSMatchingFlags flags, BOOL *stop){

            fileName = [tagsubstringWithRange: match.range];

        }];

     

        //add the image for drawing

        [self.images addObject:

         [NSDictionarydictionaryWithObjectsAndKeys:

          width, @"width",

          height, @"height",

          fileName, @"fileName",

          [NSNumber numberWithInt: [aStringlength]], @"location",

          nil]

         ];

     

        //render empty space for drawing the image inthe text //1

        CTRunDelegateCallbacks callbacks;

        callbacks.version = kCTRunDelegateVersion1;

        callbacks.getAscent = ascentCallback;

        callbacks.getDescent = descentCallback;

        callbacks.getWidth = widthCallback;

        callbacks.dealloc = deallocCallback;

     

        NSDictionary* imgAttr = [[NSDictionarydictionaryWithObjectsAndKeys: //2

                                 width, @"width",

                                 height, @"height",

                                 nil] retain];

     

        CTRunDelegateRef delegate = CTRunDelegateCreate(&callbacks,imgAttr); //3

        NSDictionary *attrDictionaryDelegate =[NSDictionary dictionaryWithObjectsAndKeys:

                                               //set the delegate

                                               (id)delegate, (NSString*)kCTRunDelegateAttributeName,

                                               nil];

     

        //add a space to the text so that it can callthe delegate

        [aStringappendAttributedString:[[[NSAttributedString alloc] initWithString:@"" attributes:attrDictionaryDelegate] autorelease]];

    }

    阅读这段代码——实际上,<img>标签并不像<font>标签那么好解析。需要使用3个正则式,才能读取到<img>的三个属性:width、height和src。然后,用一个NSDictionar保存这些信息(另外再加上图片在文字中出现的位置)并添加到self.images中。

    来到第1个代码块——CTRunDelegateCallbacks 是一个结构体,结构体中包含了一些函数指针。该结构体包含了你想告诉给CTRunDelegate的一些信息。你也许猜到了,getWith调用将告诉CTRun的宽度,getAscent调用将告诉CTRun的高度等等。在这段代码中,你为这些handler提供了函数名,随后我们将为这些函数提供实现。

    代码块2非常重要——imgAttr字典保存了图片的尺寸;同时这个对象会被传递给处理函数——因此,当getAscent函数被触发时,它将收到一个imgAttr参数,同时读取图片的高度,以便传递给CoreText。

    在代码块3,CTRunDelegateCreate函数创建了委托实例并将imgAttr和指定的CTRunDelgateCallBacks进行绑定。

    下一步,我们需要创建一个字典(和前面创建字体属性是一样的),但将字体样式属性替代以委托对象。最后,我们加入了一个空格字符,以便触发委托方法和创建文本“空洞”用于绘制图片。

    接下来,你可能想到了,我们要让委托对象提供回调函数的实现。

    //inside MarkupParser.m, just above@implementation

     

    /* Callbacks */

    static void deallocCallback( void* ref ){

        [(id)ref release];

    }

    static CGFloat ascentCallback( void *ref){

        return [(NSString*)[(NSDictionary*)refobjectForKey:@"height"] floatValue];

    }

    static CGFloat descentCallback( void *ref){

        return [(NSString*)[(NSDictionary*)refobjectForKey:@"descent"] floatValue];

    }

    static CGFloat widthCallback( void* ref){

        return [(NSString*)[(NSDictionary*)refobjectForKey:@"width"] floatValue];

    }

     

    ascentCallback, descentCallback 和 widthCallback仅仅读取了字典中的对应内容返回给Core Text。deallocCallback则释放字典——当CTRunDelegate被释放时调用,因此这里是让你进行内存管理的地方。

    在为解析器增加对<img>标签的处理之后,我们还需要修改CTView。首先需要定义一个方法,将images数组传递给视图,我们可以将NSAttributedString和imgAttr一起传递给这个方法。

    //CTView.h - inside @interfacedeclaration as an ivar

    NSArray* images;

     

    //CTView.h - declare property for images

    @property (retain, nonatomic) NSArray*images;

     

    //CTView.h - add a method declaration

    -(void)setAttString:(NSAttributedString*)attString withImages:(NSArray*)imgs;

     

    //CTView.m - just below @implementation

    @synthesize images;

     

    //CTView.m - inside the dealloc method

    self.images = nil;

     

    //CTView.m - anywhere inside theimplementation

    -(void)setAttString:(NSAttributedString*)string withImages:(NSArray*)imgs

    {

        self.attString = string;

        self.images = imgs;

    }

    好了,CTView已经能够接收一个图片数组了,让我们从解析器将图片传递给它就可以了。

    转到CoreTextMagazineViewController.m,找到这行“[(CTView*)self.view setAttString: attString];”,修改为:

    [(CTView *)[self view] setAttString:attString withImages: p.images];

    MarkupParser的attrStringFromMarkup:方法将所有的图片标签解析为数据放入了self.images,也就是你现在传给CTView的东西。

    渲染图片,首先要算出图片将要显示的准确位置。计算过程如下:

    • contentView滚动时的contentOffset
    • 相对于CTView的偏移 (frameXOffset,frameYOffset)
    • CTLine 的原点坐标(CTLine和这段文本的起始位置有一个偏移量)
    • CTLine起点和CTRun起点之间的差距

    现在开始绘制图片!首先修改 CTColumnView 类:

    /inside CTColumnView.h

    //as an ivar

    NSMutableArray* images;

     

    //as a property

    @property (retain, nonatomic)NSMutableArray* images;

     

    //inside CTColumnView.m

    //after @implementation...

    @synthesize images;

     

    -(id)initWithFrame:(CGRect)frame

    {

        if ([super initWithFrame:frame]!=nil) {

            self.images =[NSMutableArray array];

        }

        return self;

    }

     

    -(void)dealloc

    {

        self.images= nil;

        [super dealloc];

    }

     

    //at the end of drawRect:

    for (NSArray* imageData in self.images) {

        UIImage* img = [imageData objectAtIndex:0];

        CGRectimgBounds = CGRectFromString([imageData objectAtIndex:1]);

        CGContextDrawImage(context, imgBounds,img.CGImage);

    }

     

    我们修改了几个地方:增加了本地变量images和对应的属性,用于持有在每个文本栏中的图片列表。为求简便,我们没有声明新的类,而是直接在images数组中存放了:

  • UIImage 对象
  • UIImage 的位置大小 - 例如图片在文本中所处的位置以及图片大小。
  •  
  • 现在,我们来计算图片的位置并将它们添加到对应的文本栏中:

    //inside CTView.h

    -(void)attachImagesWithFrame:(CTFrameRef)finColumnView:(CTColumnView*)col;

     

    //inside CTView.m

    -(void)attachImagesWithFrame:(CTFrameRef)finColumnView:(CTColumnView*)col

    {

        //drawing images

        NSArray *lines = (NSArray *)CTFrameGetLines(f);//1

     

        CGPoint origins[[lines count]];

        CTFrameGetLineOrigins(f, CFRangeMake(0, 0),origins); //2

     

        int imgIndex = 0; //3

        NSDictionary* nextImage = [self.imagesobjectAtIndex:imgIndex];

        int imgLocation = [[nextImageobjectForKey:@"location"] intValue];

     

        //find images for the current column

        CFRange frameRange =CTFrameGetVisibleStringRange(f); //4

        while ( imgLocation < frameRange.location ) {

            imgIndex++;

            if(imgIndex>=[self.images count]) return; //quit if no images for this column

            nextImage = [self.imagesobjectAtIndex:imgIndex];

            imgLocation =[[nextImage objectForKey:@"location"] intValue];

        }

     

        NSUInteger lineIndex = 0;

       for (id lineObj inlines) { //5

            CTLineRef line =(CTLineRef)lineObj;

     

            for (id runObj in(NSArray *)CTLineGetGlyphRuns(line)) { //6

               CTRunRef run = (CTRunRef)runObj;

               CFRange runRange = CTRunGetStringRange(run);

     

               if (runRange.location <= imgLocation &&runRange.location+runRange.length > imgLocation ) { //7

               CGRect runBounds;

               CGFloat ascent;//height above the baseline

               CGFloat descent;//height below the baseline

                runBounds.size.width= CTRunGetTypographicBounds(run, CFRangeMake(0, 0), &ascent, &descent,NULL); //8

               runBounds.size.height = ascent + descent;

     

               CGFloat xOffset = CTLineGetOffsetForStringIndex(line,CTRunGetStringRange(run).location, NULL); //9

               runBounds.origin.x = origins[lineIndex].x + self.frame.origin.x +xOffset + frameXOffset;

               runBounds.origin.y = origins[lineIndex].y + self.frame.origin.y +frameYOffset;

               runBounds.origin.y -= descent;

     

                   UIImage *img = [UIImage imageNamed: [nextImageobjectForKey:@"fileName"] ];

                   CGPathRef pathRef = CTFrameGetPath(f); //10

                   CGRect colRect = CGPathGetBoundingBox(pathRef);

     

                   CGRect imgBounds = CGRectOffset(runBounds, colRect.origin.x -frameXOffset - self.contentOffset.x, colRect.origin.y - frameYOffset -self.frame.origin.y);

                   [col.images addObject: //11

                       [NSArray arrayWithObjects:img, NSStringFromCGRect(imgBounds) , nil]

                    ];

                   //load the next image //12

                   imgIndex++;

                   if (imgIndex < [self.images count]) {

                       nextImage = [self.images objectAtIndex: imgIndex];

                       imgLocation = [[nextImage objectForKey: @"location"]intValue];

                   }

     

               }

            }

            lineIndex++;

        }

    }

    注:这段代码基于DavidBeck的提供的代码而来,非常感谢David Beck!

    这段代码不是很好阅读,但你千万再忍耐一下——本文即将结束,我们都即将解脱!

    这段代码解说如下:

  • CTFrameGetLines 返回一个 CTLine 对象数组。
  • 获取当前帧所有行的起点坐标:每行文本的左上角。
  • 通过nextImage字典加载图片数据,然后从nextImage字典中读取图片在文本中的位置放入imgLocation变量。
  • CTFrameGetVisibleStringRange 从返回本帧文本的可视范围——当前你正在渲染的是哪部分文字,然后对图片数组进行迭代,知道查找到位于本帧的第1张图片。即快速找到本帧要渲染的图片的位置。
  • 对行进行迭代,加载每一行到line变量。
  • 对line中的CTRun进行迭代(通过 CTLineGetGlyphRuns获得行中的CTRun)。
  • 判断nextImage是否在CTRun的范围内——如果是,则需要在这里渲染图片。
  • 用 CTRunGetTypographicBounds 获得CTRun宽高。
  • 用CTLineGetOffsetForStringIndex计算CTRun与CTLine原点的距离。
  • 加载图片,获得当前帧矩形并计算出图片的矩形。
  • 将UIImage和它的frame放入一个NSArray,再将NSArray保存到CTColumnView的images数组。
  • 设置nextImage为下一张图片(如果存在),继续循环,直至本行结束。
  • 好了!还有一个小地方:在 CTView.m 中找到行 “[content setCTFrame:(id)frame];”,在前面插入:

    [self attachImagesWithFrame:frame inColumnView: content];

    最后一件事,你需要提供一些大文本以供测试。

    不用担心,我已经为你准备好了下一期的“僵尸月刊”——一个关于僵尸的大众月刊,你只需要:

  • 在项目导航窗口,删除test.txt
  • 从这里下载并解压缩文件: 僵尸杂志。
  • 把所有文件拖到你的Xcode项目中,注意选中“Copy items……”,然后点击Finish。
  • 打开CoreTextMagazineViewController.m ,找到获取test.txt文件路径的一行,替换为:

    NSString *path = [[NSBundle mainBundle] pathForResource:@"zombies" ofType:@"txt"];

    编译运行,开始享受最新一期的僵尸月刊吧!

    还剩最后一环节。就是使文本自动适应栏宽。加入代码:

    //在CTView.m文件

    // setAttString:withImages: 方法最后

     

    CTTextAlignment alignment =kCTJustifiedTextAlignment;

     

    CTParagraphStyleSetting settings[] = {

        {kCTParagraphStyleSpecifierAlignment,sizeof(alignment), &alignment},

    };

    CTParagraphStyleRef paragraphStyle =CTParagraphStyleCreate(settings, sizeof(settings) / sizeof(settings[0]));

    NSDictionary *attrDictionary =[NSDictionary dictionaryWithObjectsAndKeys:

                                   (id)paragraphStyle, (NSString*)kCTParagraphStyleAttributeName,

                                   nil];

    NSMutableAttributedString* stringCopy =[[[NSMutableAttributedString alloc] initWithAttributedString:self.attString]autorelease];

    [stringCopy addAttributes:attrDictionaryrange:NSMakeRange(0, [attString length])];

    self.attString =(NSAttributedString*)stringCopy;

    这会让你开始使用段落格式, 在苹果Core Text文档中查看 kCTParagraphStyleSpecifierAlignment ,可以找到你能使用的所有段落样式。

    什么时候使用 Core Text ?为什么要用 Core Text?

    现在你的Core Text杂志应用程序已经完成了,你可能会问自己:为什么要用CoreText而不是UIWebView呢?

    CT和UIWebView有各自的应用场景。

    UIWebView是一个成熟的web浏览器,仅仅为了显示一行由多个颜色组成文字未免有点杀鸡用牛刀了。

    如果你的UI上有10个多色标签,那你岂不是要用10个Safari?那会占用多少内存?

    请记住:UIWebView是一个好的浏览器,而CoreText是一个高效的文本渲染引擎。

    Core Text还能做些什么?

    这个教程中使用的完整示例在这里下载: CoreText example project 。

    如果你想在这个项目上进行扩展,并了解更多Core Text的内容,请阅读苹果的 CoreText Reference Collection 。然后考虑在这个程序中加入以下特性:

    • 为解析器添加更多标签的支持
    • 为CTRun增加更多的格式
    • 增加更多的段落格式
    • 为单词、段落和句子增加自适应格式
    • 连体字及字距支持

    我想,你可能已经想到如何去扩展解析器了,我有两个建议:

    • 解析HTML可以使用并扩展 Ben Reeves的Obj-C HTML Parser;
    自定义语法解析器,你可以使用 Obj-C ParseKit





        
    最新技术文章:
    ▪Android开发之登录验证实例教程
    ▪Android开发之注册登录方法示例
    ▪Android获取手机SIM卡运营商信息的方法
    ▪Android实现将已发送的短信写入短信数据库的...
    ▪Android发送短信功能代码
    ▪Android根据电话号码获得联系人头像实例代码
    ▪Android中GPS定位的用法实例
    ▪Android实现退出时关闭所有Activity的方法
    ▪Android实现文件的分割和组装
    ▪Android录音应用实例教程
    ▪Android双击返回键退出程序的实现方法
    ▪Android实现侦听电池状态显示、电量及充电动...
    ▪Android获取当前已连接的wifi信号强度的方法
    ▪Android实现动态显示或隐藏密码输入框的内容
    ▪根据USER-AGENT判断手机类型并跳转到相应的app...
    ▪Android Touch事件分发过程详解
    ▪Android中实现为TextView添加多个可点击的文本
    ▪Android程序设计之AIDL实例详解
    ▪Android显式启动与隐式启动Activity的区别介绍
    ▪Android按钮单击事件的四种常用写法总结
    ▪Android消息处理机制Looper和Handler详解
    ▪Android实现Back功能代码片段总结
    ▪Android实用的代码片段 常用代码总结
    ▪Android实现弹出键盘的方法
    ▪Android中通过view方式获取当前Activity的屏幕截...
    javascript开源软件 iis7站长之家
    ▪Android提高之多方向抽屉实现方法
    ▪Android提高之MediaPlayer播放网络音频的实现方法...
    ▪Android提高之MediaPlayer播放网络视频的实现方法...
    ▪Android提高之手游转电视游戏的模拟操控
     


    站内导航:


    特别声明:169IT网站部分信息来自互联网,如果侵犯您的权利,请及时告知,本站将立即删除!

    ©2012-2021,,E-mail:www_#163.com(请将#改为@)

    浙ICP备11055608号-3