I have reviewed the WWDC2020 video explaining how to adopt the new PHPickerViewController API. I have also seen several blogs showing exactly the following code.
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
if let result = results.first, result.itemProvider.canLoadObject(ofClass: UIImage.self) {
result.itemProvider.loadObject(ofClass: UIImage.self) { (image, error) in
DispatchQueue.main.async {
self.parent.didCapture(image)
}
}
} else {
parent.didCapture(nil)
}
}
However, this fails for me and the error is quite bizarre.
UIImage must confirm to _ObjectiveCBridgable
I will include a screenshot because it is quite unbelievable
Hardware: M1 chipset
IDE: Xcode 12.4
Type cast image as UIImage before using, that should solve the issue.
if let typeCastedImage = image as? UIImage {
self.parent.didCapture(typeCastedImage)
}
It depends on how you work with the values inside the function body. There are two implementations for the loadObject() function. One of them can actually accept UIImage.self. In my case, the following code works:
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
if let itemProvider = results.first?.itemProvider, itemProvider.canLoadObject(ofClass: UIImage.self) {
itemProvider.loadObject(ofClass: UIImage.self) { image, error in
if let image = image as? UIImage {
DispatchQueue.main.async {
self.imageViewField.image = image
}
}
}
}
}
It is necessary to make a check for conformance to the type of UIImage.
Related
I am trying to display a picker where users can pick a Photo or a Video. I can only find explanations on how to respond to users picking a Photo. I'm not too familiar with UIKit so can someone help me out?
This is my picker function:
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
picker.dismiss(animated: true)
guard let provider = results.first?.itemProvider else {return}
if provider.canLoadObject(ofClass: UIImage.self) {
provider.loadObject(ofClass: UIImage.self) { image, _ in
self.parent.image = image as? UIImage
}
}
}
I’m using the following code that gets triggered via a button to display the share sheet in my app:
func shareSheet() {
guard let urlShare = URL(string: "https://google.com") else { return }
let activityVC = UIActivityViewController(activityItems: [urlShare], applicationActivities: nil)
UIApplication.shared.windows.first?.rootViewController?.present(activityVC, animated: true, completion: nil)
}
This code brings up the warning:
'windows' was deprecated in iOS 15.0: Use UIWindowScene.windows on a relevant window scene instead
I don't understand how I can get rid of it. Already checked the approach suggested here How to get rid of message " 'windows' was deprecated in iOS 15.0: Use UIWindowScene.windows on a relevant window scene instead" with AdMob banner? but it's not working. Any ideas how I can bypass the warning?
Many Thanks!
You can use following as UIApplication.shared.currentUIWindow()?.rootViewController
public extension UIApplication {
func currentUIWindow() -> UIWindow? {
let connectedScenes = UIApplication.shared.connectedScenes
.filter({
$0.activationState == .foregroundActive})
.compactMap({$0 as? UIWindowScene})
let window = connectedScenes.first?
.windows
.first { $0.isKeyWindow }
return window
}
}
So I have an image view titled cashOrCredit and have am trying to set it's image programatically but somehow not able to.
First I set the image like this
cell.cashOrCredit.image = UIImage(named: "cash1.png")
and I get an error saying a separator of "," is needed.
Then I tried it this way
var cashImage: UIImage?
cashImage = "cash1.png"
cell.cashOrCredit.image = cashImage
But I get a THREAD 1 EXC BAD INSTRUCTION error.
I can't seem to understand what is going wrong ?
Here is the error
Updated for Swift 3:
use below simple code, to set the image to UIImageView;
class YourViewControllerName: UIViewController {
var mYourImageViewOutlet: UIImageView?
func addImageToUIImageView{
var yourImage: UIImage = UIImage(named: "Birthday_logo")!
mYourImageViewOutlet.image = yourImage
} // call this function where you want to set image.
}
Note: "Birthday_logo" type of image must be present in your Assets.xcassets of your project.
I attached the screenshot if you want any help please refer it.
****// used anywhere you want to add an image to UIImageView. [Here I used one function & in that function, I write a code to set image to UIImageView]****
Enjoy..!
Try this:
cell.cashOrCredit.image = UIImage(named: "cash1")
and check "cash1.png" image is available in Assets.xcassets or not.
If you get solution, then give upvote to my answer.
Delete ".png":
cell.cashOrCredit.image = UIImage(named: "cash1")
You can also set it all programmatically:
let cellImage = UIImageView(frame: CGRect(x: X, y: Y, width: WIDTH, height: HEIGHT))
cellImage.image = UIImage(named: "cash1")
cell.addSubview(cellImage)
Take Outlet of ImageView
#IBOutlet weak var imgProfile: UIImageView!
Go through the following code which contains will be helpful you to pick image or capture image from the camera.
func choosePicker() {
let alertController = UIAlertController(title: "Select Option", message: nil, preferredStyle: (IS_IPAD ? UIAlertControllerStyle.alert : UIAlertControllerStyle.actionSheet))
let cancelAction = UIAlertAction(title: "Cancel", style: UIAlertActionStyle.cancel, handler: { action -> Void in
})
let gallery = UIAlertAction(title: "From Gallery", style: UIAlertActionStyle.default
, handler: { action -> Void in
self.openPicker(isCamera: false)
})
let camera = UIAlertAction(title: "Take Photo", style: UIAlertActionStyle.default
, handler: { action -> Void in
self.openPicker(isCamera: true)
})
alertController.addAction(gallery)
alertController.addAction(camera)
alertController.addAction(cancelAction)
self.present(alertController, animated: true, completion: nil)
}
func openPicker(isCamera : Bool) {
let picker:UIImagePickerController?=UIImagePickerController()
if isCamera {
picker!.sourceType = UIImagePickerControllerSourceType.camera
} else {
picker!.sourceType = UIImagePickerControllerSourceType.photoLibrary
picker!.allowsEditing = true
}
picker?.delegate = self
if UIDevice.current.userInterfaceIdiom == .phone {
self.present(picker!, animated: true, completion: nil)
}
else {
picker!.modalPresentationStyle = .popover
present(picker!, animated: true, completion: nil)//4
picker!.popoverPresentationController?.sourceView = imgProfile
picker!.popoverPresentationController?.permittedArrowDirections = UIPopoverArrowDirection.up
}
}
// MARK: - UIImagePickerControllerDelegate Methods
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
if let image = info[UIImagePickerControllerOriginalImage] as? UIImage {
self.imgProfile.image = image
}
picker.dismiss(animated: true, completion: nil);
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
dismiss(animated: true, completion: nil)
}
Call choosePicker method wherever you want to call.
Check if cashOrCredit is uiimageview.
Secondly,
cashImage = UIImage(named: "cash1.png")
If that doesnot work, try
cell?. cashOrCredit.image
check if cell is nil?
In the context of Swift code, EXC_BAD_INSTRUCTION usually means you’ve hit a compiler trap, that is, an undefined instruction inserted into the code by the compiler because of a bug detected at runtime. The most common cause of these are:
failure to unwrap an optional — This can be a forced unwrap (!) or an implicit unwrap (accessing an implicitly unwrapped optional that’s nil).
array out of bounds
a failed forced cast (as!), either because the value was a nil optional or because the value was of the wrong type
To play videos in my project.I have created Custom video player(i.e AVplayerLayer instead of AVPlayerViewController) for custom controls.The problem is when i try to take a screenshot of videoPlayer it returns black image.
This is the code i have used to take a screenshot
func captureScreen() -> UIImage? {
UIGraphicsBeginImageContextWithOptions(view.bounds.size, false, UIScreen.main.scale)
view.layer.render(in: UIGraphicsGetCurrentContext()!)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
Any suggestions would be greatly helpful!
If you need to take a snapshot of a view containing an AVPlayerLayer, to use in animations/freeze states, you can also use view.snapshotView(afterScreenUpdates:).
This returns a view with a screenshot of your view.
Handy in combination with UIVisualEffectView! :)
Try this,hope it will help you...
extension UIView
{
public func getSnapshotImage() -> UIImage
{
UIGraphicsBeginImageContextWithOptions(self.bounds.size, self.isOpaque, 0)
self.drawHierarchy(in: self.bounds, afterScreenUpdates: false)
let snapshotImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return snapshotImage
}
}
Usage:
let Snap = self.VideoPlayerView.getSnapshotImage
Since you are using AVPlayer rather than AVAssetReader+AVAssetReaderTrackOutput(which can take screen shot just along with playing)
I guess this will help you.
if let item = player.currentItem {
let imageGenerator = AVAssetImageGenerator(asset: item.asset)
imageGenerator.appliesPreferredTrackTransform = true
if let cgImage = try? imageGenerator.copyCGImage(at: item.currentTime(), actualTime: nil) {
let image = UIImage(cgImage: cgImage)
}
}
TIP: You can get that item at you own way. (e. the same when you create the AVPlayer)
The assumptions just for safty which compiler indicates you. (You can forced unwrapping of course)
I'm using UICollectionView to show the images in my app.
The problem is that it takes very slow to show images. After 50 seconds, the images in collection view appears. :(
When I find the solution in google, mostly they write the following codes. But it is not work for me.
cell.layer.shouldRasterize = true
cell.layer.rasterizationScale = UIScreen.main.scale
and
extension SeeAllCollectionView {
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
debugPrint("seeAllLIStCell Count \(assetsTable.count)")
return assetsTable.count
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "seeAllListCell", for: indexPath) as! SeeAllPhotoCell
let list = assetsTable[(indexPath as NSIndexPath).row]
var imageName: String? = (list.poster_image_url)
var image: UIImage? = (images_cache[imageName!])
if image != nil {
debugPrint("Yes Image")
cell.imageView.image = image
} else{
debugPrint("NO Image")
cell.imageView.image = nil
DispatchQueue.main.async(){
let url = NSURL(string: list.poster_image_url)
let data = NSData(contentsOf:url! as URL)
var image = UIImage(data: data as! Data)
DispatchQueue.main.async(execute: {() -> Void in
cell.movieTitle.text = list.name
cell.imageView?.image = image
})
self.images_cache[imageName!] = image
}
}
return cell
}
}
// MARK: - UICollectionViewDelegate
extension SeeAllCollectionView {
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
debugPrint("Selected")
let list = assetsTable[(indexPath as NSIndexPath).row]
debugPrint(list.poster_image_url)
debugPrint(list.name)
prefs.set(list.poster_image_url, forKey: "poster_image_url")
prefs.set(list.name, forKey: "name")
prefs.set(list.assets_id, forKey: "VIDEO_ID")
prefs.set(false, forKey: "FLAG")
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "DetailsChannel") as UIViewController
self.present(vc, animated: true, completion: nil)
}
}
Here is my screenshot when I run the project. I got these many lines of codes when I run.
Please anyone help me how should I do?
I got the same console error when I am getting the data from the API call and reload the UITableView (as per my requirement). My issue is solved by using
DispatchQueue.global(qos: .background).async { // load data in back ground mode so that main thread can be safed.
let url = NSURL(string: list.poster_image_url)
let data = NSData(contentsOf:url! as URL)
var image = UIImage(data: data as! Data)
DispatchQueue.main.async(execute: {
cell.movieTitle.text = list.name
cell.imageView?.image = image
})
self.images_cache[imageName!] = image
}
Screen Shot of Error I got on my Console Before
You can give an attempt to SDWebimage All async Thread operations maintained well.
That can provide following advantages
Asynchronously download
Auto Purging Image Cache if memory warnings happen for the app
Image URL caching
Image Caching
Avoid Duplicate Downloads
You can directly use a single method in cell as below
[cell.storeImg sd_setImageWithURL:[NSURL URLWithString:strURL] placeholderImage:kDefaultImageForDisplay];
In your code following line should creates problem as that operation can happen on main thread
let data = NSData(contentsOf:url! as URL)
this line:
DispatchQueue.main.async()
upon downloading image is probably causing the error, it blocks the main thread and executes network requests on main (UI) queue, not to mention the fact that these requests are then performed serially (therefore slow). Try to change the snippet to:
DispatchQueue.global.async(qos: DispatchQoS.QoSClass.background){
let url = NSURL(string: list.poster_image_url)
let data = NSData(contentsOf:url! as URL)
var image = UIImage(data: data as! Data)
DispatchQueue.main.async(execute: {() -> Void in
where global stands for global queue default for such network operations.
EDIT: apart from using main queue for network calls there may be an issue with actually too many images loaded for rows which are not currently visible on the screen. If there are lot of them and connection is not-so-good you will end up with app pending downloads for onscreen cells. Consider lazy loading images only for onscreen cells - and cancelling downloads for rows which are not visible. There is a quite good tutorial (however for table view, but you can easily extend it for collection) in Swift on how to accomplish this.