2013-01-08 10 views
17

UILabelのドロップキャップの最初の文字は、attributedTextNSAttributedStringプロパティのみを使用しています。このように:NSAttributedStringを使用したドロップキャップ

http://www.interpretationbydesign.com/wp-content/uploads/2009/02/drop_caps.gif

私は負の値に最初の文字の範囲のためのベースラインを調整することで実験してきた、そしてそれは、残りの上で最初の文字の上部を揃えるために働きます最初の行しかし、私は他の行をドロップキャップの文字の右側に流す方法を見つけていません。

これはNSAttributedString onlyを使用して解決できますか、または文字列を分割してコアテキストを使用して自分自身でレンダリングする必要がありますか?

+0

これまでに達成したもののスクリーンショットを貼ることができますか?テストコード? –

+1

私は自分のアプリケーションでドロップキャップ文字列を使用していますが、それを行うには、私はUIWebViewを使用し、その効果を行うためにHTMLを使用しました。私はそれがUILabelでできることを確信していません – Youssef

答えて

7

CoreTextはグリフランからなる行で構成されているため、ドロップキャップは使用できません。ドロップキャップは、サポートされていない複数の行をカバーします。

この効果を達成するには、キャップを別々に描画し、残りのテキストを周囲のパスに描画する必要があります。

UILabelでは長い話はできませんが、可能ですが、CoreTextでかなりの作業があります。 CoreTextでそれを行うには

手順は次のとおりです。

  • は、単一文字のframesetterを作成します。
  • その境界
  • は、ドロップキャップ
  • のフレームを惜しみパスを作成します。このパス
  • で残りの文字のためのframesetterを作成
  • が残り
+0

私はUILabelでそれをするつもりはありません、私はコアテキストでそれをしたいだけですが、 'NSAttributesString'を使用しています。いくつかのフレームセッター、またはパスを持つフレームセッターではありません。 – PeyloW

+0

私が言ったように、それは単一の帰属文字列では不可能です。フレームセッティングの仕組みを理解するには、CoreTextのイントロを参照してください。 http://www.cocoanetics.com/2011/01/befriending-core-text/ – Cocoanetics

1
を描く最初のグリフを描きます

完璧な解決策ではありませんが、DTCoreTextを試して、通常のNSStringformatted HTMLとしてください。 HTML内では、 "キャップをドロップ"することが可能です。

3

いいえ、これはNSAttributedStringと標準の文字列のみでは実行できません。

ドロップキャップは段落のプロパティなので、CTParagraphStyleにはドロップキャップに関する情報が含まれている必要があります。段落の開始のインデントに影響するCTParagraphStyleの唯一のプロパティはkCTParagraphStyleSpecifierFirstLineHeadIndentですが、これは最初の行にのみ影響します。

CTFramesetterに2番目以降の行の始まりを計算する方法はありません。

唯一の方法は、独自の属性を定義し、このカスタム属性を認識するCTFramesetterCTTypesetterを使用して文字列を描画するコードを記述することです。

14

誰もが触れたように、NSAttributedStringでこれを行うことはできません。 Nikolaiは、CTFrameSettersを使用して正しいアプローチをしています。しかしながら、であり、フレームセッターに特定の領域(すなわち、CGPathによって定義される)にテキストをレンダリングするように指示することが可能である。

2つのフレームセッターを作成する必要があります.1つはドロップキャップ用、もう1つはテキスト用です。

次に、ドロップキャップのフレームをつかんで、ドロップキャップの枠の周りを走るCGPathRefを作ります。

次に、両方のフレームセッターをビューにレンダリングします。

私は、UIViewのサブクラスであるDropCapViewというオブジェクトを持つサンプルプロジェクトを作成しました。このビューは、最初の文字をレンダリングし、残りのテキストをその周囲にラップします。

dropcap on ios

はかなりの数のステップがありますので、私は例をホスティングgithubのプロジェクトへのリンクを追加しました:

それはこのようになります。プロジェクトにはあなたを助けるコメントがあります。

DropCap project on GitHub

あなたは、ビューのエッジの周りのパディングのためtextBox素子の形状(すなわちCGPathRef)で遊んでする必要があります、とだけでなく、ドロップキャップ文字にそれを強化します。ここで

は、描画方法の根性です:

- (void)drawRect:(CGRect)rect { 
    //make sure that all the variables exist and are non-nil 
    NSAssert(_text != nil, @"text is nil"); 
    NSAssert(_textColor != nil, @"textColor is nil"); 
    NSAssert(_fontName != nil, @"fontName is nil"); 
    NSAssert(_dropCapFontSize > 0, @"dropCapFontSize is <= 0"); 
    NSAssert(_textFontSize > 0, @"textFontSize is <=0"); 

    //convert the text aligment from NSTextAligment to CTTextAlignment 
    CTTextAlignment ctTextAlignment = NSTextAlignmentToCTTextAlignment(_textAlignment); 

    //create a paragraph style 
    CTParagraphStyleSetting paragraphStyleSettings[] = { { 
      .spec = kCTParagraphStyleSpecifierAlignment, 
      .valueSize = sizeof ctTextAlignment, 
      .value = &ctTextAlignment 
     } 
    }; 

    CFIndex settingCount = sizeof paragraphStyleSettings/sizeof *paragraphStyleSettings; 
    CTParagraphStyleRef style = CTParagraphStyleCreate(paragraphStyleSettings, settingCount); 

    //create two fonts, with the same name but differing font sizes 
    CTFontRef dropCapFontRef = CTFontCreateWithName((__bridge CFStringRef)_fontName, _dropCapFontSize, NULL); 
    CTFontRef textFontRef = CTFontCreateWithName((__bridge CFStringRef)_fontName, _textFontSize, NULL); 

    //create a dictionary of style elements for the drop cap letter 
    NSDictionary *dropCapDict = [NSDictionary dictionaryWithObjectsAndKeys: 
           (__bridge id)dropCapFontRef, kCTFontAttributeName, 
           _textColor.CGColor, kCTForegroundColorAttributeName, 
           style, kCTParagraphStyleAttributeName, 
           @(_dropCapKernValue) , kCTKernAttributeName, 
           nil]; 
    //convert it to a CFDictionaryRef 
    CFDictionaryRef dropCapAttributes = (__bridge CFDictionaryRef)dropCapDict; 

    //create a dictionary of style elements for the main text body 
    NSDictionary *textDict = [NSDictionary dictionaryWithObjectsAndKeys: 
           (__bridge id)textFontRef, kCTFontAttributeName, 
           _textColor.CGColor, kCTForegroundColorAttributeName, 
           style, kCTParagraphStyleAttributeName, 
           nil]; 
    //convert it to a CFDictionaryRef 
    CFDictionaryRef textAttributes = (__bridge CFDictionaryRef)textDict; 

    //clean up, because the dictionaries now have copies 
    CFRelease(dropCapFontRef); 
    CFRelease(textFontRef); 
    CFRelease(style); 

    //create an attributed string for the dropcap 
    CFAttributedStringRef dropCapString = CFAttributedStringCreate(kCFAllocatorDefault, 
                    (__bridge CFStringRef)[_text substringToIndex:1], 
                    dropCapAttributes); 

    //create an attributed string for the text body 
    CFAttributedStringRef textString = CFAttributedStringCreate(kCFAllocatorDefault, 
                   (__bridge CFStringRef)[_text substringFromIndex:1], 
                    textAttributes); 

    //create an frame setter for the dropcap 
    CTFramesetterRef dropCapSetter = CTFramesetterCreateWithAttributedString(dropCapString); 

    //create an frame setter for the dropcap 
    CTFramesetterRef textSetter = CTFramesetterCreateWithAttributedString(textString); 

    //clean up 
    CFRelease(dropCapString); 
    CFRelease(textString); 

    //get the size of the drop cap letter 
    CFRange range; 
    CGSize maxSizeConstraint = CGSizeMake(200.0f, 200.0f); 
    CGSize dropCapSize = CTFramesetterSuggestFrameSizeWithConstraints(dropCapSetter, 
                     CFRangeMake(0, 1), 
                     dropCapAttributes, 
                     maxSizeConstraint, 
                     &range); 

    //create the path that the main body of text will be drawn into 
    //i create the path based on the dropCapSize 
    //adjusting to tighten things up (e.g. the *0.8,done by eye) 
    //to get some padding around the edges of the screen 
    //you could go to +5 (x) and self.frame.size.width -5 (same for height) 
    CGMutablePathRef textBox = CGPathCreateMutable(); 
    CGPathMoveToPoint(textBox, nil, dropCapSize.width, 0); 
    CGPathAddLineToPoint(textBox, nil, dropCapSize.width, dropCapSize.height * 0.8); 
    CGPathAddLineToPoint(textBox, nil, 0, dropCapSize.height * 0.8); 
    CGPathAddLineToPoint(textBox, nil, 0, self.frame.size.height); 
    CGPathAddLineToPoint(textBox, nil, self.frame.size.width, self.frame.size.height); 
    CGPathAddLineToPoint(textBox, nil, self.frame.size.width, 0); 
    CGPathCloseSubpath(textBox); 

    //create a transform which will flip the CGContext into the same orientation as the UIView 
    CGAffineTransform flipTransform = CGAffineTransformIdentity; 
    flipTransform = CGAffineTransformTranslate(flipTransform, 
               0, 
               self.bounds.size.height); 
    flipTransform = CGAffineTransformScale(flipTransform, 1, -1); 

    //invert the path for the text box 
    CGPathRef invertedTextBox = CGPathCreateCopyByTransformingPath(textBox, 
                    &flipTransform); 
    CFRelease(textBox); 

    //create the CTFrame that will hold the main body of text 
    CTFrameRef textFrame = CTFramesetterCreateFrame(textSetter, 
                CFRangeMake(0, 0), 
                invertedTextBox, 
                NULL); 
    CFRelease(invertedTextBox); 
    CFRelease(textSetter); 

    //create the drop cap text box 
    //it is inverted already because we don't have to create an independent cgpathref (like above) 
    CGPathRef dropCapTextBox = CGPathCreateWithRect(CGRectMake(_dropCapKernValue/2.0f, 
                   0, 
                   dropCapSize.width, 
                   dropCapSize.height), 
                &flipTransform); 
    CTFrameRef dropCapFrame = CTFramesetterCreateFrame(dropCapSetter, 
                 CFRangeMake(0, 0), 
                 dropCapTextBox, 
                 NULL); 
    CFRelease(dropCapTextBox); 
    CFRelease(dropCapSetter); 

    //draw the frames into our graphic context 
    CGContextRef gc = UIGraphicsGetCurrentContext(); 
    CGContextSaveGState(gc); { 
     CGContextConcatCTM(gc, flipTransform); 
     CTFrameDraw(dropCapFrame, gc); 
     CTFrameDraw(textFrame, gc); 
    } CGContextRestoreGState(gc); 
    CFRelease(dropCapFrame); 
    CFRelease(textFrame); 
} 

P.S.これはいくつかのインスピレーションから来ています:https://stackoverflow.com/a/9272955/1218605

関連する問題