将更改的图像保存到相机胶卷时因内存问题而终止

时间:2016-12-09 02:06:01

标签: ios swift uiimagepickercontroller

我正在构建一个允许用户为照片添加时间戳的应用。当选择来自相机胶卷的照片时,我的应用程序有时会崩溃并且Xcode会显示错误:"来自调试器的消息:由于内存问题而终止"。我发现如果我尝试将时间戳添加到一个非常大的图像,或者如果我在同一图像的时间戳上重复添加时崩溃了。

以下是我认为与此问题相关的代码。此外,here是整个项目的链接。

这是时间戳添加到图像的方式。我把评论放在崩溃的地方。它并不总是在同一行代码上崩溃。我无法弄清楚为什么我的代码会因内存问题错误而崩溃。

  func textToImage(drawText text: NSString, inImage image: UIImage, atPoint point: CGPoint) -> UIImage {

    let color = UserDefaults.standard.colorForKey(key: "color")!
    let font = UserDefaults.standard.value(forKey: "font") as! String
    let size = UserDefaults.standard.value(forKey: "size") as! Int
    let fontAndSize = UIFont(name: font, size: CGFloat(size))!
    let location = Locations(rawValue: UserDefaults.standard.value(forKey: "location") as! String)!
    let scale = UIScreen.main.scale


    UIGraphicsBeginImageContextWithOptions(image.size, false, scale)
    // sometimes terminates here

    let textFontAttributes = [
      NSFontAttributeName: fontAndSize,
      NSForegroundColorAttributeName: color
      ] as [String : Any]
    image.draw(in: CGRect(origin: CGPoint.zero, size: image.size))

    let rectSize = text.boundingRect(with: CGSize(width:CGFloat(MAXFLOAT), height: CGFloat(MAXFLOAT)), options: NSStringDrawingOptions.usesLineFragmentOrigin, attributes: textFontAttributes, context: nil).size
    // sometimes terminates here
    if location == .topRight || location == .bottomRight || location == .center || location == .topCenter || location == .bottomCenter {
      // Calculate the text size

      if location == .center || location == .topCenter || location == .bottomCenter {
        // Subtract half the text width from the x origin
        let rect = CGRect(x:point.x-(rectSize.width / 2), y:point.y-(rectSize.height / 2), width:rectSize.width, height: rectSize.height)
        text.draw(in: rect, withAttributes: textFontAttributes)
      } else {
        // Subtract the text width from the x origin
        let rect = CGRect(x:point.x-rectSize.width, y:point.y-(rectSize.height / 2), width:rectSize.width, height: rectSize.height)
        text.draw(in: rect, withAttributes: textFontAttributes)
      }
    } else {
      let rect = CGRect(x:point.x, y:point.y-(rectSize.height / 2), width:rectSize.width, height: rectSize.height)
      text.draw(in: rect, withAttributes: textFontAttributes)
    }

    let newImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()

    return newImage!
  }

  func selectPhotoFromCameraRoll(mediaType: String) {
    imagePicker.sourceType = .photoLibrary
    if mediaType == "Video" {
      imagePicker.mediaTypes = [kUTTypeMovie as String]
    }
    newMedia = false
    present(imagePicker, animated: true, completion: nil)
  }

  func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
    if let mediaType = info[UIImagePickerControllerMediaType] as? String {
      if mediaType.isEqual((kUTTypeImage as String)) {
        if let pickedImage = info[UIImagePickerControllerOriginalImage] as? UIImage {
          let width = pickedImage.size.width
          let height = pickedImage.size.height
          let location = Locations(rawValue: UserDefaults.standard.value(forKey: "location") as! String)!
          var point = CGPoint(x: 0, y: 0)
          switch location {
          case .topLeft:
            point = CGPoint(x: 30, y: 50)
          case .topRight:
            point = CGPoint(x: width - 30, y: 50)
          case .bottomLeft:
            point = CGPoint(x: 30, y: height - 50)
          case .bottomRight:
            point = CGPoint(x: width - 30, y: height - 50)
          case .center:
            point = CGPoint(x: width / 2, y: height / 2)
          case .topCenter:
            point = CGPoint(x: width / 2, y: 50)
          case .bottomCenter:
            point = CGPoint(x: width / 2, y: height - 50)
          }
          let savedFormat = UserDefaults.standard.value(forKey: "format") as! String
          var date = Date()
          if !currentDateBool {
            date = UserDefaults.standard.value(forKey: "selectedDate") as! Date
          }
          let timestampText = getFormattedDateFromFormatType(formatType: savedFormat, date: date) as NSString
          let timestampImage = textToImage(drawText: timestampText, inImage: pickedImage, atPoint: point)
          if newMedia {
            UIImageWriteToSavedPhotosAlbum(timestampImage, self, #selector(image(_:didFinishSavingWithError:contextInfo:)), nil)
          } else {
            UIImageWriteToSavedPhotosAlbum(timestampImage, self, #selector(image(_:didFinishSavingWithError:contextInfo:)), nil)
          }
        }
      } else if mediaType.isEqual((kUTTypeMovie as String)) {
        if let videoUrl = info[UIImagePickerControllerMediaURL] as? NSURL {
          if let videoPath = videoUrl.relativePath {
            if newMedia {
              UISaveVideoAtPathToSavedPhotosAlbum(videoPath, nil, nil, nil)
            }
          }
        }
      }
    }
    dismiss(animated: true, completion: nil)
  }


  func image(_ image: UIImage, didFinishSavingWithError error: NSError?, contextInfo: UnsafeRawPointer) {
    if let error = error {
      // we got back an error!
      let ac = UIAlertController(title: "Save error", message: error.localizedDescription, preferredStyle: .alert)
      ac.addAction(UIAlertAction(title: "OK", style: .default))
      present(ac, animated: true)
    } else {
      let ac = UIAlertController(title: "Saved!", message: "Your altered image has been saved to your photos.", preferredStyle: .alert)
      ac.addAction(UIAlertAction(title: "OK", style: .default))
      present(ac, animated: true)
    }
  }

1 个答案:

答案 0 :(得分:0)

您正在将相机胶卷中的照片视为UIImage。它不是。

让我们采取最糟糕的情况。假设我们有1200万像素的摄像头。然后你的派生UIImage将是4032x3024像素。现在,您要求使用缩放屏幕分辨率制作图形上下文。假设该设备的屏幕分辨率为3倍。然后你要的是图形上下文12096x9072,即109734912.那是。要存储颜色数据,您必须乘以每个点的颜色信息占用的空间。我不知道那是多少,但我们肯定会上升另一个数量级。无论如何,无论你如何分割,这都是你需要的大量记忆。