質問編集履歴

3

文字の追加

2021/03/18 05:46

投稿

downtowntown
downtowntown

スコア21

test CHANGED
File without changes
test CHANGED
@@ -339,3 +339,9 @@
339
339
  }
340
340
 
341
341
  ```
342
+
343
+
344
+
345
+ ### 試したこと
346
+
347
+ NSMutableParagraphStyleのlineSpacing,paragraphSpacingを使って試したが変わらなかった

2

コードの追加

2021/03/18 05:46

投稿

downtowntown
downtowntown

スコア21

test CHANGED
File without changes
test CHANGED
@@ -128,10 +128,6 @@
128
128
 
129
129
  titleLayer.backgroundColor = UIColor.white.cgColor
130
130
 
131
- titleLayer.string = "Dummy text\nDummy text"
132
-
133
- titleLayer.font = UIFont(name: "Helvetica", size: 28)
134
-
135
131
  titleLayer.isWrapped = true
136
132
 
137
133
  titleLayer.truncationMode = .end
@@ -140,6 +136,20 @@
140
136
 
141
137
  titleLayer.frame = CGRect(x: 0, y: 50, width: size.width, height: size.height / 6)
142
138
 
139
+ var attributes: [NSAttributedString.Key: Any] = [.foregroundColor: UIColor.white, .font: UIFont.systemFont(ofSize: 26, weight: .bold)]
140
+
141
+ let paragraphStyle = NSMutableParagraphStyle()
142
+
143
+ paragraphStyle.lineSpacing = 0
144
+
145
+ paragraphStyle.alignment = .center
146
+
147
+ attributes.updateValue(paragraphStyle, forKey: .paragraphStyle)
148
+
149
+ let attributeString = NSAttributedString(string:"DuummyText\nDummuyText", attributes: attributes)
150
+
151
+ textLayer.string = attributeString
152
+
143
153
 
144
154
 
145
155
 

1

脱字

2021/03/18 02:14

投稿

downtowntown
downtowntown

スコア21

test CHANGED
File without changes
test CHANGED
@@ -1,359 +1,331 @@
1
1
  ### 前提・実現したいこと
2
2
 
3
+ 動画にテキストを合成してカメラロールに保存するときにCATextLayerの行間を狭めて保存したい
4
+
5
+
6
+
7
+ ### Swift
8
+
9
+
10
+
11
+ ```ここに言語名を入力
12
+
13
+ import UIKit
14
+
15
+ import AVFoundation
16
+
17
+ import AVKit
18
+
19
+ import Photos
20
+
21
+
22
+
23
+ class ViewController: UIViewController {
24
+
25
+
26
+
27
+ var myurl: URL?
28
+
29
+
30
+
31
+ override func viewDidLoad() {
32
+
33
+ super.viewDidLoad()
34
+
35
+ }
36
+
37
+
38
+
39
+ @IBAction func saveVideoTapper(_ sender: Any) {
40
+
41
+
42
+
43
+ let path = Bundle.main.path(forResource: "sample_video", ofType:"mp4")
44
+
45
+ let fileURL = NSURL(fileURLWithPath: path!)
46
+
47
+
48
+
49
+ let composition = AVMutableComposition()
50
+
51
+ let vidAsset = AVURLAsset(url: fileURL as URL, options: nil)
52
+
53
+
54
+
55
+ // get video track
56
+
57
+ let vtrack = vidAsset.tracks(withMediaType: AVMediaType.video)
58
+
59
+ let videoTrack: AVAssetTrack = vtrack[0]
60
+
61
+ let vid_timerange = CMTimeRangeMake(start: CMTime.zero, duration: vidAsset.duration)
62
+
63
+
64
+
65
+ let tr: CMTimeRange = CMTimeRange(start: CMTime.zero, duration: CMTime(seconds: 10.0, preferredTimescale: 600))
66
+
67
+ composition.insertEmptyTimeRange(tr)
68
+
69
+
70
+
71
+ let trackID:CMPersistentTrackID = CMPersistentTrackID(kCMPersistentTrackID_Invalid)
72
+
73
+
74
+
75
+ if let compositionvideoTrack: AVMutableCompositionTrack = composition.addMutableTrack(withMediaType: AVMediaType.video, preferredTrackID: trackID) {
76
+
77
+
78
+
79
+ do {
80
+
81
+ try compositionvideoTrack.insertTimeRange(vid_timerange, of: videoTrack, at: CMTime.zero)
82
+
83
+ } catch {
84
+
85
+ print("error")
86
+
87
+ }
88
+
89
+
90
+
91
+ compositionvideoTrack.preferredTransform = videoTrack.preferredTransform
92
+
93
+
94
+
3
- 動画にテキストを
95
+ } else {
96
+
4
-
97
+ print("unable to add video track")
98
+
5
-
99
+ return
100
+
6
-
101
+ }
102
+
103
+
104
+
105
+
106
+
107
+ // Watermark Effect
108
+
109
+ let size = videoTrack.naturalSize
110
+
111
+
112
+
113
+ let imglogo = UIImage(named: "image.png")
114
+
115
+ let imglayer = CALayer()
116
+
117
+ imglayer.contents = imglogo?.cgImage
118
+
119
+ imglayer.frame = CGRect(x: 5, y: 5, width: 100, height: 100)
120
+
121
+ imglayer.opacity = 0.6
122
+
123
+
124
+
125
+ // create text Layer
126
+
127
+ let titleLayer = CATextLayer()
128
+
129
+ titleLayer.backgroundColor = UIColor.white.cgColor
130
+
131
+ titleLayer.string = "Dummy text\nDummy text"
132
+
133
+ titleLayer.font = UIFont(name: "Helvetica", size: 28)
134
+
135
+ titleLayer.isWrapped = true
136
+
137
+ titleLayer.truncationMode = .end
138
+
139
+ titleLayer.alignmentMode = CATextLayerAlignmentMode.center
140
+
141
+ titleLayer.frame = CGRect(x: 0, y: 50, width: size.width, height: size.height / 6)
142
+
143
+
144
+
145
+
146
+
147
+ let videolayer = CALayer()
148
+
149
+ videolayer.frame = CGRect(x: 0, y: 0, width: size.width, height: size.height)
150
+
151
+
152
+
153
+ let parentlayer = CALayer()
154
+
155
+ parentlayer.frame = CGRect(x: 0, y: 0, width: size.width, height: size.height)
156
+
157
+ parentlayer.addSublayer(videolayer)
158
+
159
+ parentlayer.addSublayer(imglayer)
160
+
161
+ parentlayer.addSublayer(titleLayer)
162
+
163
+
164
+
165
+ let layercomposition = AVMutableVideoComposition()
166
+
167
+ layercomposition.frameDuration = CMTimeMake(value: 1, timescale: 30)
168
+
169
+ layercomposition.renderSize = size
170
+
171
+ layercomposition.animationTool = AVVideoCompositionCoreAnimationTool(postProcessingAsVideoLayer: videolayer, in: parentlayer)
172
+
173
+
174
+
175
+ // instruction for watermark
176
+
177
+ let instruction = AVMutableVideoCompositionInstruction()
178
+
179
+ instruction.timeRange = CMTimeRangeMake(start: CMTime.zero, duration: composition.duration)
180
+
181
+ let videotrack = composition.tracks(withMediaType: AVMediaType.video)[0] as AVAssetTrack
182
+
183
+ let layerinstruction = AVMutableVideoCompositionLayerInstruction(assetTrack: videotrack)
184
+
185
+ instruction.layerInstructions = NSArray(object: layerinstruction) as [AnyObject] as! [AVVideoCompositionLayerInstruction]
186
+
187
+ layercomposition.instructions = NSArray(object: instruction) as [AnyObject] as! [AVVideoCompositionInstructionProtocol]
188
+
189
+
190
+
191
+ // create new file to receive data
192
+
193
+ let dirPaths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
194
+
195
+ let docsDir = dirPaths[0] as NSString
196
+
197
+ let movieFilePath = docsDir.appendingPathComponent("result.mov")
198
+
199
+ let movieDestinationUrl = NSURL(fileURLWithPath: movieFilePath)
200
+
201
+
202
+
203
+ // use AVAssetExportSession to export video
204
+
205
+ let assetExport = AVAssetExportSession(asset: composition, presetName:AVAssetExportPresetHighestQuality)
206
+
207
+ assetExport?.outputFileType = AVFileType.mov
208
+
209
+ assetExport?.videoComposition = layercomposition
210
+
211
+
212
+
213
+ // Check exist and remove old file
214
+
215
+ FileManager.default.removeItemIfExisted(movieDestinationUrl as URL)
216
+
217
+
218
+
219
+ assetExport?.outputURL = movieDestinationUrl as URL
220
+
221
+ assetExport?.exportAsynchronously(completionHandler: {
222
+
223
+ switch assetExport!.status {
224
+
225
+ case AVAssetExportSession.Status.failed:
226
+
227
+ print("failed")
228
+
229
+ print(assetExport?.error ?? "unknown error")
230
+
231
+ case AVAssetExportSession.Status.cancelled:
232
+
233
+ print("cancelled")
234
+
235
+ print(assetExport?.error ?? "unknown error")
236
+
237
+ default:
238
+
239
+ print("Movie complete")
240
+
241
+
242
+
243
+ self.myurl = movieDestinationUrl as URL
244
+
245
+
246
+
247
+ PHPhotoLibrary.shared().performChanges({
248
+
249
+ PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: movieDestinationUrl as URL)
250
+
7
- ### 発生している問題・エラーメッセージ
251
+ }) { saved, error in
252
+
8
-
253
+ if saved {
254
+
9
-
255
+ print("Saved")
256
+
257
+ }
258
+
259
+ }
260
+
261
+
262
+
263
+ self.playVideo()
264
+
265
+
266
+
267
+ }
268
+
269
+ })
270
+
271
+
272
+
273
+ }
274
+
275
+
276
+
277
+
278
+
279
+ func playVideo() {
280
+
281
+ let player = AVPlayer(url: myurl!)
282
+
283
+ let playerLayer = AVPlayerLayer(player: player)
284
+
285
+ playerLayer.frame = self.view.bounds
286
+
287
+ self.view.layer.addSublayer(playerLayer)
288
+
289
+ player.play()
290
+
291
+ print("playing...")
292
+
293
+ }
294
+
295
+
296
+
297
+
298
+
299
+
300
+
301
+ }
302
+
303
+
304
+
305
+
306
+
307
+ extension FileManager {
308
+
309
+ func removeItemIfExisted(_ url:URL) -> Void {
310
+
311
+ if FileManager.default.fileExists(atPath: url.path) {
312
+
313
+ do {
314
+
315
+ try FileManager.default.removeItem(atPath: url.path)
316
+
317
+ }
318
+
319
+ catch {
320
+
321
+ print("Failed to delete file")
322
+
323
+ }
324
+
325
+ }
326
+
327
+ }
328
+
329
+ }
10
330
 
11
331
  ```
12
-
13
- エラーメッセージ
14
-
15
- ```
16
-
17
-
18
-
19
- ### Swift
20
-
21
-
22
-
23
- ```ここに言語名を入力
24
-
25
- import UIKit
26
-
27
- import AVFoundation
28
-
29
- import AVKit
30
-
31
- import Photos
32
-
33
-
34
-
35
- class ViewController: UIViewController {
36
-
37
-
38
-
39
- var myurl: URL?
40
-
41
-
42
-
43
- override func viewDidLoad() {
44
-
45
- super.viewDidLoad()
46
-
47
- }
48
-
49
-
50
-
51
- @IBAction func saveVideoTapper(_ sender: Any) {
52
-
53
-
54
-
55
- let path = Bundle.main.path(forResource: "sample_video", ofType:"mp4")
56
-
57
- let fileURL = NSURL(fileURLWithPath: path!)
58
-
59
-
60
-
61
- let composition = AVMutableComposition()
62
-
63
- let vidAsset = AVURLAsset(url: fileURL as URL, options: nil)
64
-
65
-
66
-
67
- // get video track
68
-
69
- let vtrack = vidAsset.tracks(withMediaType: AVMediaType.video)
70
-
71
- let videoTrack: AVAssetTrack = vtrack[0]
72
-
73
- let vid_timerange = CMTimeRangeMake(start: CMTime.zero, duration: vidAsset.duration)
74
-
75
-
76
-
77
- let tr: CMTimeRange = CMTimeRange(start: CMTime.zero, duration: CMTime(seconds: 10.0, preferredTimescale: 600))
78
-
79
- composition.insertEmptyTimeRange(tr)
80
-
81
-
82
-
83
- let trackID:CMPersistentTrackID = CMPersistentTrackID(kCMPersistentTrackID_Invalid)
84
-
85
-
86
-
87
- if let compositionvideoTrack: AVMutableCompositionTrack = composition.addMutableTrack(withMediaType: AVMediaType.video, preferredTrackID: trackID) {
88
-
89
-
90
-
91
- do {
92
-
93
- try compositionvideoTrack.insertTimeRange(vid_timerange, of: videoTrack, at: CMTime.zero)
94
-
95
- } catch {
96
-
97
- print("error")
98
-
99
- }
100
-
101
-
102
-
103
- compositionvideoTrack.preferredTransform = videoTrack.preferredTransform
104
-
105
-
106
-
107
- } else {
108
-
109
- print("unable to add video track")
110
-
111
- return
112
-
113
- }
114
-
115
-
116
-
117
-
118
-
119
- // Watermark Effect
120
-
121
- let size = videoTrack.naturalSize
122
-
123
-
124
-
125
- let imglogo = UIImage(named: "image.png")
126
-
127
- let imglayer = CALayer()
128
-
129
- imglayer.contents = imglogo?.cgImage
130
-
131
- imglayer.frame = CGRect(x: 5, y: 5, width: 100, height: 100)
132
-
133
- imglayer.opacity = 0.6
134
-
135
-
136
-
137
- // create text Layer
138
-
139
- let titleLayer = CATextLayer()
140
-
141
- titleLayer.backgroundColor = UIColor.white.cgColor
142
-
143
- titleLayer.string = "Dummy text\nDummy text"
144
-
145
- titleLayer.font = UIFont(name: "Helvetica", size: 28)
146
-
147
- titleLayer.isWrapped = true
148
-
149
- titleLayer.truncationMode = .end
150
-
151
- titleLayer.alignmentMode = CATextLayerAlignmentMode.center
152
-
153
- titleLayer.frame = CGRect(x: 0, y: 50, width: size.width, height: size.height / 6)
154
-
155
-
156
-
157
-
158
-
159
- let videolayer = CALayer()
160
-
161
- videolayer.frame = CGRect(x: 0, y: 0, width: size.width, height: size.height)
162
-
163
-
164
-
165
- let parentlayer = CALayer()
166
-
167
- parentlayer.frame = CGRect(x: 0, y: 0, width: size.width, height: size.height)
168
-
169
- parentlayer.addSublayer(videolayer)
170
-
171
- parentlayer.addSublayer(imglayer)
172
-
173
- parentlayer.addSublayer(titleLayer)
174
-
175
-
176
-
177
- let layercomposition = AVMutableVideoComposition()
178
-
179
- layercomposition.frameDuration = CMTimeMake(value: 1, timescale: 30)
180
-
181
- layercomposition.renderSize = size
182
-
183
- layercomposition.animationTool = AVVideoCompositionCoreAnimationTool(postProcessingAsVideoLayer: videolayer, in: parentlayer)
184
-
185
-
186
-
187
- // instruction for watermark
188
-
189
- let instruction = AVMutableVideoCompositionInstruction()
190
-
191
- instruction.timeRange = CMTimeRangeMake(start: CMTime.zero, duration: composition.duration)
192
-
193
- let videotrack = composition.tracks(withMediaType: AVMediaType.video)[0] as AVAssetTrack
194
-
195
- let layerinstruction = AVMutableVideoCompositionLayerInstruction(assetTrack: videotrack)
196
-
197
- instruction.layerInstructions = NSArray(object: layerinstruction) as [AnyObject] as! [AVVideoCompositionLayerInstruction]
198
-
199
- layercomposition.instructions = NSArray(object: instruction) as [AnyObject] as! [AVVideoCompositionInstructionProtocol]
200
-
201
-
202
-
203
- // create new file to receive data
204
-
205
- let dirPaths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
206
-
207
- let docsDir = dirPaths[0] as NSString
208
-
209
- let movieFilePath = docsDir.appendingPathComponent("result.mov")
210
-
211
- let movieDestinationUrl = NSURL(fileURLWithPath: movieFilePath)
212
-
213
-
214
-
215
- // use AVAssetExportSession to export video
216
-
217
- let assetExport = AVAssetExportSession(asset: composition, presetName:AVAssetExportPresetHighestQuality)
218
-
219
- assetExport?.outputFileType = AVFileType.mov
220
-
221
- assetExport?.videoComposition = layercomposition
222
-
223
-
224
-
225
- // Check exist and remove old file
226
-
227
- FileManager.default.removeItemIfExisted(movieDestinationUrl as URL)
228
-
229
-
230
-
231
- assetExport?.outputURL = movieDestinationUrl as URL
232
-
233
- assetExport?.exportAsynchronously(completionHandler: {
234
-
235
- switch assetExport!.status {
236
-
237
- case AVAssetExportSession.Status.failed:
238
-
239
- print("failed")
240
-
241
- print(assetExport?.error ?? "unknown error")
242
-
243
- case AVAssetExportSession.Status.cancelled:
244
-
245
- print("cancelled")
246
-
247
- print(assetExport?.error ?? "unknown error")
248
-
249
- default:
250
-
251
- print("Movie complete")
252
-
253
-
254
-
255
- self.myurl = movieDestinationUrl as URL
256
-
257
-
258
-
259
- PHPhotoLibrary.shared().performChanges({
260
-
261
- PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: movieDestinationUrl as URL)
262
-
263
- }) { saved, error in
264
-
265
- if saved {
266
-
267
- print("Saved")
268
-
269
- }
270
-
271
- }
272
-
273
-
274
-
275
- self.playVideo()
276
-
277
-
278
-
279
- }
280
-
281
- })
282
-
283
-
284
-
285
- }
286
-
287
-
288
-
289
-
290
-
291
- func playVideo() {
292
-
293
- let player = AVPlayer(url: myurl!)
294
-
295
- let playerLayer = AVPlayerLayer(player: player)
296
-
297
- playerLayer.frame = self.view.bounds
298
-
299
- self.view.layer.addSublayer(playerLayer)
300
-
301
- player.play()
302
-
303
- print("playing...")
304
-
305
- }
306
-
307
-
308
-
309
-
310
-
311
-
312
-
313
- }
314
-
315
-
316
-
317
-
318
-
319
- extension FileManager {
320
-
321
- func removeItemIfExisted(_ url:URL) -> Void {
322
-
323
- if FileManager.default.fileExists(atPath: url.path) {
324
-
325
- do {
326
-
327
- try FileManager.default.removeItem(atPath: url.path)
328
-
329
- }
330
-
331
- catch {
332
-
333
- print("Failed to delete file")
334
-
335
- }
336
-
337
- }
338
-
339
- }
340
-
341
- }
342
-
343
- ```
344
-
345
-
346
-
347
- ### 試したこと
348
-
349
-
350
-
351
- ここに問題に対して試したことを記載してください。
352
-
353
-
354
-
355
- ### 補足情報(FW/ツールのバージョンなど)
356
-
357
-
358
-
359
- ここにより詳細な情報を記載してください。