How to make AVPlayer & AVPlayerLayer scrollable (AVFoundation) - ios

I am using a collectionView to display both images/videos.
I am retrieving the content from Firebase.
With the images, I can scroll. I added a scrollview and manually added 20 imageViews as Firebase do not allow bulk retrieval of images/content. And then check if a value exists, and if so, display the content at a certain index (of image 1-20).
I did seem to notice when define a certain number in this piece of code:
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
I can scroll the videos. However since I cannot retrieve multiple images, I resorted to the more inconvenient manner or adding 20 imageViews. To display videos, I am doing the following:
if let firstVidURLString = post?.firstVideoURL, let firstVideoURL = URL(string: firstVidURLString) {
self.volumeView.isHidden = false
videoPlayer = AVPlayer(url: firstVideoURL)
videoPlayerLayer = AVPlayerLayer(player: videoPlayer)
videoPlayerLayer?.frame = self.firstImageView.frame
videoPlayerLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill
layoutIfNeeded()
videoPlayerLayer?.zPosition = 0
self.contentView.layer.addSublayer(videoPlayerLayer!)
self.volumeView.layer.zPosition = 1
videoPlayer?.play()
videoPlayer?.isMuted = isMuted
}
I am happy with how it is placed, I have defined the frame/bounds in correlation to the first image view I have. Surely, that should help me scroll? As it takes up the same size as one image view. Alas, the video player/video layer is just static and will not conform to the scrollview. Any ideas on how I can tackle this?
Thank you.

Figured it out. This is for anyone else, in the future.
Connect your scrollView, if you have one, to the corresponding .swift file i.e. ViewController, CollectionViewCell etc.
Once you do that, simply add this line.
self.[your scrollview].layer.addSublayer(the name of the video layer !)
And you'll be fine!

Related

Detecting which ImageView has has been tapped

So i have 4 vStacks, each containing 9 ImageViews. Each ImageView represents one Card, alpha 0 is default. When a Card is Detected (with ARKit), my code sets the ImageView to alpha 1 so the user can see that the card has been scanned.
Now: I want to implement that when the user clicks on one of the ImageViews, an alert should pop up asking the user if he is sure he wants to delete the scanned card. My problem is, I have no idea what the best practice is to get the information that the card has been tapped and how to delete it without hardcoding.
In ViewDidLoad i set the images into the ImageVies like This:
//This repeats for all 36 ImageViews
imgView1.image = UIImage(named: "Eichel_6")
imgView2.image = UIImage(named: "Eichel_7")
/*When a image is detected with ARKit, this is what happens. Basically
*it pushes the corresponding reference name to an array called
* scannedCards, handles them, and removes them afterwards.
* spielPoints = gamepoints/points, spielmodus = gamemode
*/
func updateLabel() {
//print all cards in scanned cards
for card in scannedCards {
points += (DataController.shared.spielpoints[DataController.shared.spielmodus!]![card]! * DataController.shared.multCalculator[DataController.shared.spielmodus!]!)
}
scannedCards.removeAll()
}
I am a new to coding, I would be grateful if you correct me if my code snippets are bad, beside my question. Thank you in advance.
As has already been mentioned in comments, you should use a UICollectionView for this kind of work. #Fogmeister has promised to add an answer concerning that later, so I won't do that. But I can answer the actual question, even though it's not what you should do.
From your code I can see that you probably have outlets for all your imageViews (imgView1 ... imgView36) and set each image manually. To detect taps on any of these, you could do something like this:
func viewDidLoad(){
super.viewDidLoad()
let allImageViews = [imageView1, imageView2, .... imageView36]
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(didTapImageView(gesture:)))
allImageViews.forEach({$0.addGestureRecognizer(tapGestureRecognizer)})
}
#objc func didTapImageView(gesture:UITapGestureRecognizer){
guard let imageView = gesture.view as? UIImageView else { return }
//Here you can put code that will happen regardless of which imageView was tapped.
imageView.alpha = 0.0
//If you need to know exactly which imageView was tapped, you can just check
if imageView == self.imageView1{
//Do stuff only for imageView1
}else if imageView == self.imageView2{
//...
}//....
}
Again, this is not very good practice. If you go for UICollectionView instead, you don't have to have outlets for all your imageViews and you don't have to create a gestureRecognizer for handling events. But still, I hope this helped you understand general gestures better.

setting bounds.height in ios 13 not working anymore

Since the iOS update to 13 my app is unfortunately behaving bad.
The containers height is controlled by the UISegmentControl element.
The hook works fine and the correct index is set.
There are 3 subcontainers:
profileDataContainerView
addValueContainerView
checkInHistoryContainerView
Unfortunately the containers within the main scrollview dont change their height.
Does anyone know the solution for this issue?
#IBAction func showComponent(_ sender: UISegmentedControl) {
showComponent(index: sender.selectedSegmentIndex)
}
private func showComponent(index: Int) {
currentContainer?.bounds.size.height = 0
currentSegmentedControllerIndex = index
switch index {
case 0:
profileDataContainerView.bounds.size.height = 308
currentContainer = profileDataContainerView
case 1:
addValueContainerView.bounds.size.height = 294
currentContainer = addValueContainerView
case 2:
if let tableView = checkInHistoryContainerView.subviews.last as? UITableView {
checkInHistoryContainerView.bounds.size.height = tableView.contentSize.height
}
currentContainer = checkInHistoryContainerView
default:
break
}
updateContentHeight()
}
You can use yourview.layer.bounds instead of yourview.bounds.
It's because it's a struct.
this will work
myView.frame = Frame(....)
this will not
myView.bounds.height = someNumber
It is hard to say for sure why your app is not working with just the code provided, but you can keep in mind a few things.
A scroll view size is adjusted automatically by its content. Try adjusting the scrollview itself, see this link: how to set scrollview content size in swift 3.0
Adjust the content size and then reload the view afterwards. Try to make sure that your app is loading all of your views after you set the new size. This link may help you with changing frame sizes: Change frame programmatically with auto layout
In general storyboards are a disaster for things like this if you aren't careful. Storyboards tend to give the illusion of success when in fact the constraints are wrong.
One final thing to check (a bug I found within my code) is that segues have changed substantially, so make sure that your segues are the correct type and you are presenting in the way you want to. Here is a link on some of the presenting changes in iOS 13:
https://medium.com/#hacknicity/view-controller-presentation-changes-in-ios-13-ac8c901ebc4e
Edit: Chris replied mentioning that you are using a struct, if thats the case then they are correct in that you are not properly changing the value.

Swift 3 iOS 10 Reference to UIImageView to get Value for other function

I'm looking for a way to add codewise a way to reference to a UIImageView's value from another function.
I'm having a function which creates a new UIImageView for each instance in an array. Moreover, it also has an UITapGestureRecognizer added which calls a function when tapped. Now I want the function, which is called upon tap, to get the instance of that array which relates to the respective UIImageView.
I hope it's clear what I want to do. :)
Let's say you have an array of images:
let myImages = [UIImage(named: "image1"), UIImage(named: image2", UIImage(named: "image3"0]
And you have these images in three separate image views:
imageViewA.image = myImages[0]
imageViewB.image = myImages[1]
imageViewC.image = myImages[2]
Also, you've set up everything correctly that each image view calls the same function on a tap (we'll call it imageViewTapped()).
All you need to do is (1) properly set the tag property of each image view to point at the array index, (2) get the view that was tapped on, and (3) retrieve the image.
imageViewA.tag = 0
imageViewB.tag = 1
imageViewC.tag = 2
func imageViewTapped(_ recognizer:UITapGestureRecognizer) {
let tappedView = recognizer.view
let sourceImage = myImages[tappedView.tag]
}
I'd recommend setting everything up in a loop (tags, tap recognizer) and adding some type-casting in the function if needed. But I can't see your code to help you with that.

How to detect which image has been tapped in swift

I have created 6 UIImageViews on a ViewController, and I am later going to add TapGestureRecognizers to all of them.
I want to make it so that depending on what image has been clicked, another ViewController will open and display certain information.
For this to happen, I need to know which image has been clicked. How would I do this in Swift?
UIGestureRecognizer has property 'view' this property is the view you add it to. For this example the imageView.
func tap(gesture: UIGestureRecognizer) {
println(gesture.view!.tag) // You can check for their tag and do different things based on tag
}
let img = UIImageView()
img.userInteraction = true
img.tag = 0
img.addGestureRecognizer(UITapGestureRecognizer(self, action: "tap:"))

TableView with Image SubView performance Issue

Ive got one question about using a subView in my custom TableViewCells. On certain rows i want to one or more images, and i am doing this programmatically with:
func addImageToCell(image: UIImage, initialYCoordinate: CGFloat, initialHeight: CGFloat, initialXCoordinate: CGFloat,imageUUID: String) {
imageButton = UIButton.buttonWithType(UIButtonType.Custom) as? UIButton
.....
imageButton!.addTarget(formVC, action: "imageButtonPressed:", forControlEvents: .TouchUpInside)
self.contentView.addSubview(imageButton!)
}
That works fine. But, when i scroll my TableView and it comes to an row with an Image, there is a small "lag". Normally it is totally smooth, but when he is trying to load this cell, there is a (maybe 0,1 seconds) lag in the software.
Is there a better way to do this programmatically without any lags? Ill load my Images on initializing the UITableViewController from a CoreData Fetch.
This is my cellForRowAtIndexPath
if(cellObject.hasImages == true) {
cell.qIndex = indexPath.row
var initialXCoordinate:CGFloat = 344.0
let questionImages = formImages[cellObject.qIndex] as [String: Images]!
for (key,image) in questionImages {
let thumbnailImage = UIImage(data: image.image)
cell.addImageToCell(thumbnailImage, initialYCoordinate: 44.00 ,initialHeight: cellObject.rowheight + cellObject.noticeHeight!, initialXCoordinate: initialXCoordinate, imageUUID: image.imageUUID)
initialXCoordinate += 130
}
}
Any Ideas? Thanks in advance
Edit: Ill use this to prevent the Buttons to get reused:
override func prepareForReuse() {
super.prepareForReuse()
if(self.reuseIdentifier != "CraftInit") {
for item in self.contentView.subviews {
if(item.isKindOfClass(UIButton)) {
item.removeFromSuperview()
}
}
}
The lag is most probably caused by the allocation of new memory for UIButton, adding it to cell's contentView and loading an image into it at the time of the cell being reused. That's one problem.
The other problem is that you're creating a button per every reuse of the cell. Basically, every time you scroll the cell out of the visibility range and scroll it back - you add another button with another image, that also consumes memory and performance.
To make this work properly you need to create a custom table view cell with a maximum amount of images (buttons) pre-rendered and hide the ones you don't need to show.
Do not add / remove subviews while reusing the table view cell, hide and show them instead, it's much faster.
Where are you removing these subviews? Because if you're not removing them, as the user scroll, you'll keep on adding subviews to the same cell over and over again, degrading performance.
My approach usually is to to have such views constructed when the cell is created rather than in cellForRowAtIndexPath, but I'm guessing you can't do that as you don't know the number of images. That said, you could still formulate a strategy where these subviews are added at cell creation, and then reused as per your model.
Another suggestion is to have the UIImage objects created all at once, outside cellForRowAtIndexPath.

Resources