🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

Q&A

解決済

1回答

1886閲覧

CATextLayerの行間を狭めたい

Yeezy21

総合スコア21

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

0グッド

0クリップ

投稿2021/03/17 12:23

編集2021/03/18 05:46

前提・実現したいこと

動画にテキストを合成してカメラロールに保存するときにCATextLayerの行間を狭めて保存したい

Swift

import UIKit import AVFoundation import AVKit import Photos class ViewController: UIViewController { var myurl: URL? override func viewDidLoad() { super.viewDidLoad() } @IBAction func saveVideoTapper(_ sender: Any) { let path = Bundle.main.path(forResource: "sample_video", ofType:"mp4") let fileURL = NSURL(fileURLWithPath: path!) let composition = AVMutableComposition() let vidAsset = AVURLAsset(url: fileURL as URL, options: nil) // get video track let vtrack = vidAsset.tracks(withMediaType: AVMediaType.video) let videoTrack: AVAssetTrack = vtrack[0] let vid_timerange = CMTimeRangeMake(start: CMTime.zero, duration: vidAsset.duration) let tr: CMTimeRange = CMTimeRange(start: CMTime.zero, duration: CMTime(seconds: 10.0, preferredTimescale: 600)) composition.insertEmptyTimeRange(tr) let trackID:CMPersistentTrackID = CMPersistentTrackID(kCMPersistentTrackID_Invalid) if let compositionvideoTrack: AVMutableCompositionTrack = composition.addMutableTrack(withMediaType: AVMediaType.video, preferredTrackID: trackID) { do { try compositionvideoTrack.insertTimeRange(vid_timerange, of: videoTrack, at: CMTime.zero) } catch { print("error") } compositionvideoTrack.preferredTransform = videoTrack.preferredTransform } else { print("unable to add video track") return } // Watermark Effect let size = videoTrack.naturalSize let imglogo = UIImage(named: "image.png") let imglayer = CALayer() imglayer.contents = imglogo?.cgImage imglayer.frame = CGRect(x: 5, y: 5, width: 100, height: 100) imglayer.opacity = 0.6 // create text Layer let titleLayer = CATextLayer() titleLayer.backgroundColor = UIColor.white.cgColor titleLayer.isWrapped = true titleLayer.truncationMode = .end titleLayer.alignmentMode = CATextLayerAlignmentMode.center titleLayer.frame = CGRect(x: 0, y: 50, width: size.width, height: size.height / 6) var attributes: [NSAttributedString.Key: Any] = [.foregroundColor: UIColor.white, .font: UIFont.systemFont(ofSize: 26, weight: .bold)] let paragraphStyle = NSMutableParagraphStyle() paragraphStyle.lineSpacing = 0 paragraphStyle.alignment = .center attributes.updateValue(paragraphStyle, forKey: .paragraphStyle) let attributeString = NSAttributedString(string:"DuummyText\nDummuyText", attributes: attributes) textLayer.string = attributeString let videolayer = CALayer() videolayer.frame = CGRect(x: 0, y: 0, width: size.width, height: size.height) let parentlayer = CALayer() parentlayer.frame = CGRect(x: 0, y: 0, width: size.width, height: size.height) parentlayer.addSublayer(videolayer) parentlayer.addSublayer(imglayer) parentlayer.addSublayer(titleLayer) let layercomposition = AVMutableVideoComposition() layercomposition.frameDuration = CMTimeMake(value: 1, timescale: 30) layercomposition.renderSize = size layercomposition.animationTool = AVVideoCompositionCoreAnimationTool(postProcessingAsVideoLayer: videolayer, in: parentlayer) // instruction for watermark let instruction = AVMutableVideoCompositionInstruction() instruction.timeRange = CMTimeRangeMake(start: CMTime.zero, duration: composition.duration) let videotrack = composition.tracks(withMediaType: AVMediaType.video)[0] as AVAssetTrack let layerinstruction = AVMutableVideoCompositionLayerInstruction(assetTrack: videotrack) instruction.layerInstructions = NSArray(object: layerinstruction) as [AnyObject] as! [AVVideoCompositionLayerInstruction] layercomposition.instructions = NSArray(object: instruction) as [AnyObject] as! [AVVideoCompositionInstructionProtocol] // create new file to receive data let dirPaths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true) let docsDir = dirPaths[0] as NSString let movieFilePath = docsDir.appendingPathComponent("result.mov") let movieDestinationUrl = NSURL(fileURLWithPath: movieFilePath) // use AVAssetExportSession to export video let assetExport = AVAssetExportSession(asset: composition, presetName:AVAssetExportPresetHighestQuality) assetExport?.outputFileType = AVFileType.mov assetExport?.videoComposition = layercomposition // Check exist and remove old file FileManager.default.removeItemIfExisted(movieDestinationUrl as URL) assetExport?.outputURL = movieDestinationUrl as URL assetExport?.exportAsynchronously(completionHandler: { switch assetExport!.status { case AVAssetExportSession.Status.failed: print("failed") print(assetExport?.error ?? "unknown error") case AVAssetExportSession.Status.cancelled: print("cancelled") print(assetExport?.error ?? "unknown error") default: print("Movie complete") self.myurl = movieDestinationUrl as URL PHPhotoLibrary.shared().performChanges({ PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: movieDestinationUrl as URL) }) { saved, error in if saved { print("Saved") } } self.playVideo() } }) } func playVideo() { let player = AVPlayer(url: myurl!) let playerLayer = AVPlayerLayer(player: player) playerLayer.frame = self.view.bounds self.view.layer.addSublayer(playerLayer) player.play() print("playing...") } } extension FileManager { func removeItemIfExisted(_ url:URL) -> Void { if FileManager.default.fileExists(atPath: url.path) { do { try FileManager.default.removeItem(atPath: url.path) } catch { print("Failed to delete file") } } } }

試したこと

NSMutableParagraphStyleのlineSpacing,paragraphSpacingを使って試したが変わらなかった

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Yeezy21

2021/03/18 02:11

NSAttributedStringを使って見たのですが、NSMutableParagraphStyleの部分が適用されませんでした。
Yeezy21

2021/03/18 02:15

NSAttributeStringnに変更したコードを追加しました。
Yeezy21

2021/03/18 02:52

返信ありがとうございます。 paragraphSpacingを設定したのですが、こちらも効果なしでした。
guest

回答1

0

ベストアンサー

Apple公式情報ではありませんが、CATextLayerではNSAttributedStringのいくつかの属性が無視されるとの情報がありました。
私の方で実際に試してみると、NSParagraphStyleは無視されることが確認できました。

Apple公式では"シンプルなテキストレイアウトを提供します"とのことなので複雑な属性が適用されないのは現状では仕様なのかもしれません。
(バグの可能性もありますのでFeedback AssistantからAppleに報告することも可能です)

今回はとりあえずの解決方法として、次の方法はいかがでしょうか?

  • NSAttributedStringを画像に変換してそれを描画する。

Swift

1let paragraphStyle = NSMutableParagraphStyle() 2paragraphStyle.lineSpacing = -10 3 4let attrStr = NSAttributedString(string: "aaa\nbbb", attributes: [ 5 .foregroundColor: UIColor.black, 6 .font: UIFont.systemFont(ofSize: 50), 7 .paragraphStyle: paragraphStyle 8]) 9 10let imageSize = CGSize(width: 300, height: 300) 11UIGraphicsBeginImageContext(imageSize) 12attrStr.draw(in: CGRect(origin: .zero, size: imageSize)) 13let image = UIGraphicsGetImageFromCurrentImageContext()! 14UIGraphicsEndImageContext() 15 16let textlayer = CALayer() 17textlayer.frame = CGRect(origin: .zero, size: imageSize) 18textlayer.contents = image.cgImage 19 20parentLayer.addSublayer(textlayer)

投稿2021/03/20 10:57

errolizer

総合スコア441

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Yeezy21

2021/03/23 02:50

ありがとうございます! 画像に変換して描画する方法があるんですね とても参考になりました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問