iOS.& Swift Books iOS.Apprentice

30
图像选择器 由Eli Ganim撰写

您的所有地点的屏幕大多是功能完整 - 除了添加照片的位置的能力。修理好了!

uikit.comes with a built-in view controller, UIImagePickerController, that lets the user take new photos and videos, or pick them from their Photo Library. You’re going to use it to save a photo along with the location so the user has a nice picture to look at.

这是你的屏幕看起来什么时,你就大功告成了,如:

标签位置屏幕中的一张照片
标签位置屏幕中的一张照片

在本章中,您将执行以下操作:

  • 添加图像选择器:将图像选择器添加到您的应用程序中,允许您使用相机拍摄照片或从照片库中选择现有图像。
  • 显示图像:显示在表视图细胞所摄取的图像。
  • UI改进:将应用程序发送到后台时,请提高用户界面功能。
  • 保存映像:通过设备上的图像选择器保存选择的图像,以便稍后可以检索它。
  • 编辑图像:显示的图像编辑屏幕上如果该位置有一个图像。
  • 缩略图:显示缩略图的位置列表屏幕上的位置。

添加图像选择器

正如您需要在渗透前询问用户Formis,从设备中的GPS信息,您需要一个设备,你应该到达Avuice,你应该得到要求访问用户的照片乐趣。

您不需要为此编写任何代码,但您确实需要在应用程序中声明您的意图 info.plist.. If you don’t do this, the app will crash (with no visible warnings except for a message in the Xcode Console) as soon as you try to use the UIImagePickerController.

info.plist更改

➤打开 info.plist. 并添加新行 - 使用现有行中的加(+)按钮,或右键单击并选择 添加行 ,或使用 编辑▸添加项目 menu option.

在Info.plist中添加使用说明
OgeSk A UWudu Goljqwies og avse.fhipx

使用相机添加图像

➤in. locationDetailsViewController.swift.,下面的扩展添加到源文件的结尾:

extension LocationDetailsViewController:
    UIImagePickerControllerDelegate,
    UINavigationControllerDelegate {

  // MARK:- Image Helper Methods
  func takePhotoWithCamera() {
    let imagePicker = UIImagePickerController()
    imagePicker.sourceType = .camera
    imagePicker.delegate = self
    imagePicker.allowsEditing = true
    present(imagePicker, animated: true, completion: nil)
  }
}
// MARK:- Image Picker Delegates
func imagePickerController(_ picker: UIImagePickerController,
                          didFinishPickingMediaWithInfo info:
                   [UIImagePickerController.InfoKey : Any]) {
  dismiss(animated: true, completion: nil)
}

func imagePickerControllerDidCancel(_ picker:
                      UIImagePickerController) {
  dismiss(animated: true, completion: nil)
}
override func tableView(_ tableView: UITableView,
           didSelectRowAt indexPath: IndexPath) {
  if indexPath.section == 0 && indexPath.row == 0 {
    . . .
  } else if indexPath.section == 1 && indexPath.row == 0 {
    takePhotoWithCamera()
  }
}
*** Terminating app due to uncaught exception ’NSInvalidArgumentException’, reason: ’Source type 1 not available’
imagePicker.sourceType = .camera
相机界面
XNI Lamuzo Ebzuqgopa.

使用照片库添加图像

您还可以测试模拟图像选择器,而是使用相机,你必须使用照片库。

func choosePhotoFromLibrary() {
  let imagePicker = UIImagePickerController()
  imagePicker.sourceType = .photoLibrary
  imagePicker.delegate = self
  imagePicker.allowsEditing = true
  present(imagePicker, animated: true, completion: nil)
}
将图像添加到模拟器
avbiln uvuqif da hukowewiy

/Applications/Xcode.app/Contents/Developer/usr/bin/simctl addmedia booted ~/Desktop/MyPhoto.JPG
图书馆的照片
CRE BXILAR AD CPA yopmurt

用户可以调整照片
CVA Ekov Vig Tjiik Vza Xwogu

选择相机和照片库之间

首先,你检查相机是否可用。如果是,则显示一个 行动表 让用户在相机和图片库之间进行选择。

func pickPhoto() {
  if UIImagePickerController.isSourceTypeAvailable(.camera) {
    showPhotoMenu()
  } else {
    choosePhotoFromLibrary()
  }
}

func showPhotoMenu() {
  let alert = UIAlertController(title: nil, message: nil,
                       preferredStyle: .actionSheet)

  let actCancel = UIAlertAction(title: "Cancel", style: .cancel,
                              handler: nil)
  alert.addAction(actCancel)

  let actPhoto = UIAlertAction(title: "Take Photo",
                               style: .default, handler: nil)
  alert.addAction(actPhoto)

  let actLibrary = UIAlertAction(title: "Choose From Library",
                                 style: .default, handler: nil)
  alert.addAction(actLibrary)

  present(alert, animated: true, completion: nil)
}
动作片,让你的相机和照片库中进行选择
TGA ecmief nveav LHEX gofw审计tfeepi xakfiut biluyu ozf sfeta fifcaql

if true || UIImagePickerController.isSourceTypeAvailable(.camera) {
let actPhoto = UIAlertAction(title: "Take Photo",
      style: .default, handler: { _ in
        self.takePhotoWithCamera()
      })
let actLibrary = UIAlertAction(title: "Choose From Library",
      style: .default, handler: { _ in
        self.choosePhotoFromLibrary()
      })
tableView.deselectRow(at: indexPath, animated: true)

显示图像

Notwlike用户可以挑选照片,你应该显示它 - Xheriwise的点是什么,对吧?您将疑妥您的照片,并将照片和白色发出照片,电池将适合照片和添加照片标签将减少。

@IBOutlet weak var imageView: UIImageView!
@IBOutlet weak var addPhotoLabel: UILabel!
向添加照片单元格添加图像视图
Uduca KIOM外遇KSE EPB Dneba XACT的Evwebj

图像查看自动布局约束
Etuta Qoav Oogo Zuyeif Cezttxeotxn

var image: UIImage?
func show(image: UIImage) {
  imageView.image = image
  imageView.isHidden = false
  addPhotoLabel.text = ""
}
func imagePickerController(_ picker: UIImagePickerController,
     didFinishPickingMediaWithInfo info:
                   [UIImagePickerController.InfoKey : Any]) {

  image = info[UIImagePickerController.InfoKey.editedImage]
                                                as? UIImage
  if let theImage = image {
    show(image: theImage)
  }

  dismiss(animated: true, completion: nil)
}
照片伸展
MKA Thuki Av Blwowklup

调整表视图单元格以显示图像

➤添加新的出路图像高度约束 locationDetailsViewController.swift.:

@IBOutlet weak var imageHeight: NSLayoutConstraint!
连接约束的插座
Simbokv Mce Uifxal GIC VZA QUSFZCIOKF

func show(image: UIImage) {
  ...
  // Add the following lines
  imageHeight.constant = 260
  tableView.reloadData()
}
照片正确显示
VMI vfeko deyljatq pungedvbc

将图像设置正确显示

➤转到故事板并选择图像视图(考虑到文档大纲时,可能很难看到解锁它)。在 属性检查员,设置它 内容模式方面适合.

更改图像视图的内容模式
Xdoqluzk wji emalu ruot'b rasweyf sowu

照片的纵横比保持不变
YYE afdawl rimea什么JLE bsaxu IK HADT exmozv

UI改进

用户可以拍照 - 或选择一个 - 但是该应用程序尚未将其保存到数据存储中。在您完成此之前,仍然有一些改进来对象选择器。

处理背景模式

你看到了 清单 app that the SceneDelegate is notified by the operating system when the app is about to go to the background through its sceneDidEnterBackground(_:) method.

func listenForBackgroundNotification() {
  NotificationCenter.default.addObserver(forName:
      UIScene.didEnterBackgroundNotification,
      object: nil, queue: OperationQueue.main) { _ in

    if self.presentedViewController != nil {
      self.dismiss(animated: false, completion: nil)
    }

    self.descriptionTextView.resignFirstResponder()
  }
}

删除通知观察员

At this point, with iOS versions up to iOS 9.0, there’s one more thing you needed to do — you should tell the NotificationCenter 到 stop sending these background notifications when the Tag/Edit Location screen closes. You didn’t want NotificationCenter 到 send notifications to an object that no longer existed, that was just asking for trouble!

var observer: Any!
func listenForBackgroundNotification() {
  observer = NotificationCenter.default.addObserver(forName: . . .
deinit {
  print("*** deinit \(self)")
  NotificationCenter.default.removeObserver(observer!)
}
视图控制器和闭合之间的关系
WSA yahaguapysig xinsoin RRE yiuz romjhenduf ixn MJE sfofido

func listenForBackgroundNotification() {
  observer = NotificationCenter.default.addObserver(
    forName: UIApplication.didEnterBackgroundNotification,
    object: nil, queue: OperationQueue.main) { [weak self] _ in

    if let weakSelf = self {
      if weakSelf.presentedViewController != nil {
        weakSelf.dismiss(animated: false, completion: nil)
      }
      weakSelf.descriptionTextView.resignFirstResponder()
    }
  }
}
{ [weak self] _ in
  . . .
}

保存the image

挑照片的能力是相当无用的,如果应用程序不还节约了成本。所以,这就是你在这里做的。

数据模型更改

➤打开数据模型编辑器。添加A. Photoid. 归因于实体的位置,并给它的类型 整数32.。这是一个可选的价值 - 并非所有位置都有照片 - 所以确保 选修的 框在数据模型督察检查。

@NSManaged public var photoID: NSNumber?
var hasPhoto: Bool {
  return photoID != nil
}
var photoURL: URL {
  assert(photoID != nil, "No photo ID set")
  let filename = "Photo-\(photoID!.intValue).jpg"
  return applicationDocumentsDirectory.appendingPathComponent(
                                                     filename)
}
var photoImage: UIImage? {
  return UIImage(contentsOfFile: photoURL.path)
}
class func nextPhotoID() -> Int {
  let userDefaults = UserDefaults.standard
  let currentID = userDefaults.integer(forKey: "PhotoID") + 1
  userDefaults.set(currentID, forKey: "PhotoID")
  userDefaults.synchronize()
  return currentID
}
<x-coredata://C26CC559-959C-49F6-BEF0-F221D6F3F04A/Location/p1>

将图像保存到文件中

➤in. locationDetailsViewController.swift., in the done() method, add the following in between where you set the properties of the Location object and where you save the managed object context:

// Save image
if let image = image {
  // 1
  if !location.hasPhoto {
    location.photoID = Location.nextPhotoID() as NSNumber
  }
  // 2
  if let data = image.jpegData(compressionQuality: 0.5) {
    // 3
    do {
      try data.write(to: location.photoURL, options: .atomic)
    } catch {
      print("Error writing file: \(error)")
    }
  }
}
照片被保存在应用程序的文档文件夹
DXE npema yojaf IH JHO ohz'g Dasezuwdl gembem

@IBAction func done() {
  . . .
  if let temp = locationToEdit {
    . . .
  } else {
    . . .
    location.photoID = nil           // add this
  }
  . . .

验证SQLite中的Photo

如果您有Liya Liya或其他Sqher SQLite检测工具,您可以验证独特的照片照片照片(在Zhothoit列中):

该位置与丽雅独特PHOTOID值的对象
RZA Hiqarauq adfurzg cumx uzodoo tficuOn deduul EY LIVI

编辑图像

So far, all the changes you’ve made were for the Tag Location screen and adding new locations. Of course, you should make the Edit Location screen show the photos as well. The change to LocationDetailsViewController is quite simple.

override func viewDidLoad() {
  super.viewDidLoad()

  if let location = locationToEdit {
    title= "Edit Location"
    // New code block
    if location.hasPhoto {
      if let theImage = location.photoImage {
        show(image: theImage)
      }
    }
    // End of new code
  }
  . . .

清理位置删除

Let’s add some code to remove the photo file, if it exists, when a Location object is deleted.

func removePhotoFile() {
  if hasPhoto {
    do {
      try FileManager.default.removeItem(at: photoURL)
    } catch {
      print("Error removing file: \(error)")
    }
  }
}
override func tableView(_ tableView: UITableView,
              commit editingStyle: UITableViewCell.EditingStyle,
              forRowAt indexPath: IndexPath) {
  if editingStyle == .delete {
    let location = fetchedResultsController.object(at:
                                            indexPath)

    location.removePhotoFile()              // add this line   
    managedObjectContext.delete(location)
    . . .

缩略图

现在位置可以有照片照片,这是一个好主意在“位置”选项卡中显示主题照片。这将Lenen向上屏幕​​是一个......一块板的板,只有一堆文字是不明的令人兴奋的。

故事板改变

➤转至故事板编辑器。在该原型电池 地点 场景,从两个标签中的每一个删除前导的自动布局约束,并设置 X = 76 在里面 看法 部分 尺寸检查员.

表视图单元格具有图像视图
DBE HECDO WIAL GOKG GAR UY EWAMU CAUZ

代码更改

➤去吧 LocationCell.swift. 并添加以下方法:

func thumbnail(for location: Location) -> UIImage {
  if location.hasPhoto, let image = location.photoImage {
    return image
  }
  return UIImage()
}
if location.hasPhoto && let image = location.photoImage
photoImageView.image = thumbnail(for: location)
在位置表视图中的图像
Orafuf IV GPE Qupaxaaxz Havvo Buew

延期

So far you’ve used extensions on your view controllers to group related functionality together, such as delegate methods. But you can also use extensions to add new functionality to classes that you didn’t write yourself. That includes classes such as UIImage from the iOS frameworks.

import Foundation

extension String {
  func addRandomWord() -> String {
    let words = ["rabbit", "banana", "boat"]
    let value = Int.random(in: 0 ..< words.count)
    let word = words[value]
    return self + word
  }
}
let someString = "Hello, "
let result = someString.addRandomWord()
print("The queen says: \(result)")

通过UIImage扩展缩略图

You are going to add an extension to UIImage that lets you resize the image. You’ll use it as follows:

return image.resized(withBounds: CGSize(width: 52, height: 52))
import UIKit

extension UIImage {
  func resized(withBounds bounds: CGSize) -> UIImage {
    let horizontalRatio = bounds.width / size.width
    let verticalRatio = bounds.height / size.height
    let ratio = min(horizontalRatio, verticalRatio)
    let newSize = CGSize(width: size.width * ratio,
                         height: size.height * ratio)

    UIGraphicsBeginImageContextWithOptions(newSize, true, 0)
    draw(in: CGRect(origin: CGPoint.zero, size: newSize))
    let newImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()

    return newImage!
  }
}
func thumbnail(for location: Location) -> UIImage {
  if location.hasPhoto, let image = location.photoImage {
    return image.resized(withBounds: CGSize(width: 52,
                                           height: 52))
  }
  return UIImage()
}
这些照片被缩小到缩略图大小
CNA hkarod ERO yfjehl博XKE qata什么建筑面积lsechxoilc

缩略图现在具有正确的纵横比
DSU lpuvxniomq ZIC托索TDE fonvesf atfifp zurio

方面适合与宽度填充
OYNOCC ZEQ SW。 iysecr qizh.

处理低记忆的情况

The UIImagePickerController is very memory-hungry. Whenever the iPhone gets low on available memory, UIKit will send your app a “low memory” warning.

有技术问题吗?要报告bug? 您可以向官方书籍论坛中的书籍作者提出问题和报告错误 这里 .

有关于网上阅读体验反馈给份额? 如果您有关于UI,UX的反馈,请高在线到设计团队:

© 2021 Razeware LLC

您正在阅读的自由,以显示为本章的部分 混淆了 文本。解开这本书,我们的书籍和视频整个目录,具有raywenderlich.com专业订阅。

现在解锁

要突出或记笔记,您需要在订阅中拥有这本书或自行购买。