You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Segue传值时出现_UIPerformResizeOfTextViewForTextContainer断言失败及trimVideo函数咨询

关于Segue传值时「Assertion failure in void _UIPerformResizeOfTextViewForTextContainer」断言失败的解决办法

这个断言问题基本都和UITextView的布局逻辑冲突有关,我整理了几个常见的排查和解决方向:

  • 检查TextView的约束完整性:Segue跳转后目标控制器的TextView如果约束不清晰(比如只给了上下约束但没给宽度,或者约束冲突),布局系统在计算尺寸时就会触发断言。你可以打开Xcode的「Debug View Hierarchy」查看约束警告,确保TextView有明确的top/leading/trailing/bottom约束,或者固定宽度+高度(根据需求选择)。
  • 延迟给TextView赋值:如果在viewDidLoad里直接给TextView塞大量文本,此时视图还没完成布局,系统计算contentSize时容易出问题。可以把赋值操作挪到viewDidAppear(_:)里,或者用DispatchQueue.main.async包裹,让布局先完成再更新内容:
    DispatchQueue.main.async {
        self.textView.text = passedData
    }
    
  • 避免手动修改contentSize:如果你手动设置过TextView的contentSize或者强制修改scrollEnabled,可能会干扰系统的自动布局逻辑。尽量让系统自己管理这些属性,除非有特别明确的需求,否则别随便改。
  • 规范Segue传值时机:在prepare(for:sender:)里,只把数据存到目标控制器的属性里,不要直接操作目标控制器的TextView(比如直接赋值)。等目标控制器的视图生命周期走到合适阶段(比如viewWillAppear),再把属性里的数据赋值给TextView。

视频裁剪函数trimVideo的技术指导与完善

你给出的代码只是开头部分,我来帮你补全核心逻辑并给出优化建议:

1. 补全核心裁剪流程

首先需要完成轨道添加、时间范围设置和导出会话的配置,完整的函数逻辑大概是这样:

// 先定义TrimPoints结构体(假设你还没定义)
struct TrimPoints {
    let start: Double // 开始时间(秒)
    let end: Double   // 结束时间(秒)
}

func trimVideo(sourceURL: URL, destinationURL: URL, trimPoints: TrimPoints, completion: @escaping () -> Void) {
    guard sourceURL.isFileURL else {
        print("源URL不是文件URL")
        return
    }
    guard destinationURL.isFileURL else {
        print("目标URL不是文件URL")
        return
    }
    
    // 配置Asset,开启精确时间计算
    let options = [AVURLAssetPreferPreciseDurationAndTimingKey: true]
    let asset = AVURLAsset(url: sourceURL, options: options)
    let preferredPreset = AVAssetExportPresetPassthrough
    
    // 验证预设是否支持当前资产
    guard verifyPresetForAsset(preset: preferredPreset, asset: asset) else {
        print("当前预设不支持该资产")
        return
    }
    
    // 创建Composition和轨道
    let composition = AVMutableComposition()
    guard let videoTrack = asset.tracks(withMediaType: .video).first else {
        print("找不到视频轨道")
        return
    }
    guard let audioTrack = asset.tracks(withMediaType: .audio).first else {
        print("找不到音频轨道")
        return
    }
    
    guard let videoCompositionTrack = composition.addMutableTrack(withMediaType: .video, preferredTrackID: kCMPersistentTrackID_Invalid),
          let audioCompositionTrack = composition.addMutableTrack(withMediaType: .audio, preferredTrackID: kCMPersistentTrackID_Invalid) else {
        print("创建Composition轨道失败")
        return
    }
    
    // 设置裁剪时间范围
    let timescale = CMTimeScale(600) // 用600可以保证时间精度
    let startTime = CMTimeMakeWithSeconds(trimPoints.start, preferredTimescale: timescale)
    let endTime = CMTimeMakeWithSeconds(trimPoints.end, preferredTimescale: timescale)
    let timeRange = CMTimeRangeFromTimeToTime(startTime, endTime)
    
    // 插入时间范围到Composition轨道
    do {
        try videoCompositionTrack.insertTimeRange(timeRange, of: videoTrack, at: .zero)
        try audioCompositionTrack.insertTimeRange(timeRange, of: audioTrack, at: .zero)
    } catch {
        print("插入时间范围失败:\(error.localizedDescription)")
        return
    }
    
    // 处理目标文件覆盖问题
    do {
        if FileManager.default.fileExists(atPath: destinationURL.path) {
            try FileManager.default.removeItem(at: destinationURL)
        }
    } catch {
        print("删除旧文件失败:\(error.localizedDescription)")
        return
    }
    
    // 配置导出会话并开始导出
    guard let exportSession = AVAssetExportSession(asset: composition, presetName: preferredPreset) else {
        print("创建导出会话失败")
        return
    }
    exportSession.outputURL = destinationURL
    exportSession.outputFileType = .mp4 // 根据需求修改格式,比如.mov
    exportSession.timeRange = timeRange
    
    exportSession.exportAsynchronously {
        switch exportSession.status {
        case .completed:
            DispatchQueue.main.async {
                completion()
            }
        case .failed:
            print("导出失败:\(exportSession.error?.localizedDescription ?? "未知错误")")
        case .cancelled:
            print("导出被取消")
        default:
            break
        }
    }
}

// 实现verifyPresetForAsset辅助函数
func verifyPresetForAsset(preset: String, asset: AVAsset) -> Bool {
    guard AVAssetExportSession.allExportPresets().contains(preset) else {
        return false
    }
    return AVAssetExportSession(asset: asset, presetName: preset) != nil
}

2. 关键优化点

  • 时间精度:使用AVURLAssetPreferPreciseDurationAndTimingKey: true可以让Asset更精确地计算时间,避免裁剪出现偏差。
  • 错误处理:添加了轨道检查、文件删除、导出状态判断等错误处理,避免程序崩溃或静默失败。
  • 线程安全:导出完成后的completion回调切回了主线程,方便你后续更新UI(比如显示成功提示)。
  • 文件覆盖:导出前先删除目标路径的旧文件,否则导出会话会直接失败。

内容的提问来源于stack exchange,提问作者BigBoy1337

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 07:17:44