PHPicker Pick Video SwiftUI - swiftui

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
}
}
}

Related

SwiftUI How to share a URL to Facebook using FBSDKShareKit and ShareDialog?

I tried looking for a solution in posts such as this and this where people asked this very same question: How to share a url to Facebook using SwiftUI?
I even tried this post where somebody asked how to export a file using SwiftUI, but my problem is specifically with Facebook since I have no problem sharing urls to apps such as Whatsapp.
However, I found no answers...
UIKit
By reading Facebook's Developer documentation, I found a way to share a post using UIKit.
I created a very simple sample project to make sure I understood the topic.
Here is the sample project in UIKit:
import UIKit
import FBSDKShareKit
class ProfileViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func share(_ sender: UIButton) {
shareLink(url: URL(string: "http://www.apple.com")!)
}
func shareLink(url: URL) {
let content = ShareLinkContent()
content.contentURL = url
let dialog = ShareDialog(
fromViewController: self,
content: content,
delegate: nil
)
dialog.show()
}
}
Luckily, this worked right away.
By making this project work, I confirmed that I wasn't forgetting to link my AppBundleID to Facebook, or forgetting to add my FacebookAppID or any other needed files inside my info.plist.
SwiftUI
I then tried to replicate the same project in SwiftUI to see if I could get it to work.
I used ViewControllerRepresentable to be able to include UIActivityViewController into SwiftUI.
The reason for using UIActivityViewController is because I want the user to choose where they want to share the URL (Whatsapp, Twitter, Facebook, etc).
Here is the code:
ContentView
struct ContentView: View {
#State var showSharingView = false
var body: some View {
Button("Share Link") {
showSharingView.toggle()
}
.sheet(isPresented: $showSharingView) {
ActivityViewController(activityItems: [URL(string: "https://www.apple.com/")!])
}
}
}
UIViewControllerRepresentable
struct ActivityViewController: UIViewControllerRepresentable {
var activityItems: [URL]
var applicationActivities: [UIActivity]? = nil
func makeUIViewController(context: UIViewControllerRepresentableContext<ActivityViewController>) -> UIActivityViewController {
let controller = UIActivityViewController(activityItems: activityItems, applicationActivities: applicationActivities)
controller.completionWithItemsHandler = { (activityType, completed, returnedItems, error) in
if activityType == .postToFacebook {
shareLink(from: activityItems.first!)
}
}
return controller
}
func updateUIViewController(_ uiViewController: UIActivityViewController, context: UIViewControllerRepresentableContext<ActivityViewController>) {}
func shareLink(from url: URL) {
// controller was created so I would have a UIViewControllerType to put as a parameter for fromViewController in ShareDialog, even though I don't think it fits
let controller = UIActivityViewController(activityItems: activityItems, applicationActivities: applicationActivities)
let content = ShareLinkContent()
content.contentURL = url
let dialog = ShareDialog(fromViewController: controller, content: content, delegate: nil)
dialog.show()
}
}
This code doesn't work because of what I assume is the controller variable used when initializing ShareDialog. (self doesn't work either because "XCode cannot convert value of type 'ActivityViewController' to expected argument type 'UIViewController?'")
Question
Facebook Developer's documentation tells me that I need to write the following code in order to share a link:
guard let url = URL(string: "https://developers.facebook.com") else {
// handle and return
}
let content = ShareLinkContent()
content.contentURL = url
let dialog = ShareDialog(
viewController: self, //<--this has been changed to 'fromViewController'
content: content,
delegate: self
)
dialog.show()
However, SwiftUI doesn't work with ViewControllers. How can I create a ViewController to use as a parameter in fromViewController in ShareDialog in order to successfully share my URL to Facebook?
Use like this,
guard let url = URL(string: "https://developers.facebook.com") else {
// handle and return
}
let content = ShareLinkContent()
content.contentURL = url
let dialog = ShareDialog(
viewController: UIApplication.shared.windows.first!.rootViewController,
content: content,
delegate: UIApplication.shared.windows.first!.rootViewController
)
dialog.show()

UIImage must conform to _ObjectiveCBridgeable in PHPickerViewControllerDelegate callback

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.

Swift 3 submit form - UITextField changes only after focusing field again

I am working on a login view and trying to change the border color of a UITextField in Xcode/swift3 when validation of the textfield fails. The UITextField should get a red border color.
The problem is that if enter an email, then a password and then press the submit button, i have to focus email text field again before it gets a red border.
This is my LoginViewController.swift so far:
import Foundation
import UIKit
class LoginViewController : UIViewController, UITextFieldDelegate {
#IBOutlet weak var userEmailTextField: UITextField!
#IBOutlet weak var userPasswordTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
}
// login button action
#IBAction func loginButtonTabbed(_ sender: Any) {
// getting values from text fields
let userEmail = userEmailTextField.text;
let userPassword = userPasswordTextField.text;
// set enpoind data
let requestURL = NSURL(string: Constants.apiUrl)
//creating a task to send the post request
var request = URLRequest(url: requestURL as! URL)
request.httpMethod = "POST"
let postString = "cmd=addUser&email="+userEmail!+"&password="+userPassword!
request.httpBody = postString.data(using: .utf8)
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data, error == nil else { // check for fundamental networking error
print("error=\(error)")
return
}
if let httpStatus = response as? HTTPURLResponse, httpStatus.statusCode != 200 { // check for http errors
print("statusCode should be 200, but is \(httpStatus.statusCode)")
print("response = \(response)")
}
do {
let json = try? JSONSerialization.jsonObject(with: data, options: [])
// store json response to dictionary
if let dictionary = json as? [String: Any] {
// check if we got validation errors
if let nestedDictionary = dictionary["validation"] as? [String: Any] {
// display validation messages on device
if let emailMsg = nestedDictionary["Email"] as? String { // change color of textfield
self.userEmailTextField.errorField()
}
}
}
} catch let error as NSError {
print(error)
}
}
//executing the task
task.resume()
}
}
and the UITextField extension UITextField.swift:
import Foundation
import UIKit
extension UITextField {
func errorField(){
self.layer.borderColor = UIColor(red: 255/255.0, green: 59/255.0, blue: 48/255.0, alpha: 1.0).cgColor
self.layer.borderWidth = 1.0;
}
}
When you're doing a network call, it always happens in the background...so in order to do any kind of UI updates you need to be on the main queue. Just put the self.userEmailTextField.errorField() inside DispatchQueue.main.async {...} so it would be done immediately.
Also haven't really tested your code very well. Why?
Even in your current code the border would still turn red, but it turns red after almost like 6-7 seconds (it could take less or more for you)...because it's being ran from background thread.
What I don't understand is why clicking on the textField again brings the red border right away!? Here's what I'm guessing happens:
From the background thread you update the model ie change the textField color which queues the UI/view to be updated...but since we're on a background queue, that UI updated could take a few seconds to happen
But then you tapped on the textField right away and forced a super quick read of the textField and all its properties which includes the border—from main thread (actual user touches are always handled through main thread)...which even though are not yet red on the screen, but since it's red on the model it will read from it and change color to red immediately.

iOS: How to take Screen shot of AVplayer?

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)

UICollection is very slow in showing Image with Swift 3

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.