I am creating custom node to show gif file with play and pause button.
Following is my code.
import Foundation
import AsyncDisplayKit
class GifNode: ASCellNode {
var gifImageNode:ASNetworkImageNode = {
let node = ASNetworkImageNode()
node.contentMode = .scaleAspectFit
node.shouldRenderProgressImages = true
return node
}()
var playImage: ASImageNode = {
let node = ASImageNode()
node.contentMode = .scaleAspectFit
node.style.height = ASDimensionMakeWithPoints(30)
node.style.height = ASDimensionMakeWithPoints(30)
node.backgroundColor = .gray
node.image = #imageLiteral(resourceName: "play")
return node
}()
init(model:GifContent)
{
super.init()
self.automaticallyManagesSubnodes = true
let width = UIScreen.main.bounds.size.width
let height = (width * model.height) / model.width
gifImageNode.url = URL(string: "https://i.pinimg.com/originals/07/44/38/074438e7c75034df2dcf37ba1057803e.gif")
gifImageNode.style.width = ASDimensionMake(width)
gifImageNode.style.height = ASDimensionMake(height)
gifImageNode.animatedImagePaused = true
gifImageNode.addTarget(self, action: #selector(self.toggleGifPlay), forControlEvents: .touchUpInside)
}
#objc func toggleGifPlay()
{
self.gifImageNode.animatedImagePaused = !self.gifImageNode.animatedImagePaused
self.playImage.isHidden = !self.gifImageNode.animatedImagePaused
}
override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec {
let playButtonCenterSpec = ASCenterLayoutSpec(centeringOptions: .XY, sizingOptions: .minimumXY, child: self.playImage)
return ASOverlayLayoutSpec(child: gifImageNode, overlay: playButtonCenterSpec)
}
}
When I try to use gif node in another ASCellNode it does not render. I can see play button but not the actual gif file. If I try to load jpeg with same control, it works fine. Also it works fine, if I try to load gif directly and not using above class.
Not sure if I am missing anything.
I test your app, but gifImageNode.animatedImagePaused = true is disabled by default ur animation.
Related
Essentially I would like to add text fields on top of my image.
Currently, my viewDidLoad() is currently set up as such.
-Image Background (UIImage)
--Drawing Canvas
I would like it to be set up as such so that it is not possible to draw over the text fields, as I realize it is possible to add text over the image itself
-Image Background (UIImage)
---Drawing Canvas
-----TextFeild Struct
I would assume I need to use UITextField but I keep on getting this error in my simulation.
(Fatal error: Unexpectedly found nil while implicitly unwrapping an
Optional value)
In addition to this error, I'm unable to edit the scaling of my image background. I tried everything turning the imageView to content mode, and modifying the image before adding the view (scaleAspectFit/scaleAspectFill), resizing the view the only thing that seemed to work was changing the resolution of the source image.
I would like to add text fields up top right
class DrawingCanvasViewController: UIViewController {
#IBOutlet weak var nameTextField: UITextField!
lazy var canvas: PKCanvasView = {
let view = PKCanvasView()
view.drawingPolicy = .anyInput
view.minimumZoomScale = 1
view.maximumZoomScale = 1
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
lazy var toolPicker: PKToolPicker = {
let toolPicker = PKToolPicker()
toolPicker.addObserver(self)
return toolPicker
}()
var drawingData = Data()
var drawingChanged: (Data) -> Void = {_ in}
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(canvas)
canvas.backgroundColor = .clear
let iTest=UIImage(named: "Test2")
let imageView=UIImageView(image: iTest)
//This is what I have used for trying to resize my background image, although none of it seems to ever work
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.contentMode = .scaleAspectFit
view.addSubview(imageView)
view.sendSubviewToBack(imageView)
//The fatal error it gives me is from these two lines below
// view.addSubview(nameTextField)
// view.bringSubviewToFront(nameTextField)
NSLayoutConstraint.activate([
canvas.leadingAnchor.constraint(equalTo: view.leadingAnchor),
canvas.trailingAnchor.constraint(equalTo: view.trailingAnchor),
canvas.topAnchor.constraint(equalTo: view.topAnchor),
canvas.bottomAnchor.constraint(equalTo: view.bottomAnchor)])
toolPicker.setVisible(true, forFirstResponder: canvas)
toolPicker.addObserver(canvas)
canvas.delegate = self
canvas.becomeFirstResponder()
if let drawing = try? PKDrawing(data: drawingData){
canvas.drawing = drawing
}
}
}
I'm new to Xcode, I did follow a tutorial to add the pencil kit UI although am currently unable to refind it, and I figured out how to add the background image from looking at apple documentation, Hence I may have simply missed an easier/better way to code my app.
what I'm seeing from your code is: that you added UITextField in your Xib/Storyboard file. Therefore you can remove the reference of UITextField in the Xib/Storyboard file with the controller and introduce UITextField programmatically. My solution below will only address the fatal error from the 2 lines:
view.addSubview(nameTextField)
view.bringSubviewToFront(nameTextField)
Then let me know other issues when you get this one working first. Please adjust the contrainst of nameTextField to have it visible.
class DrawingCanvasViewController: UIViewController {
// initialise text field
lazy var nameTextField: UITextField = {
let textField = UITextField()
textField.translatesAutoresizingMaskIntoConstraints = false
// Please setup delegate by yourself
return textField
}()
lazy var canvas: PKCanvasView = {
let view = PKCanvasView()
view.drawingPolicy = .anyInput
view.minimumZoomScale = 1
view.maximumZoomScale = 1
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
lazy var toolPicker: PKToolPicker = {
let toolPicker = PKToolPicker()
toolPicker.addObserver(self)
return toolPicker
}()
var drawingData = Data()
var drawingChanged: (Data) -> Void = {_ in}
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(canvas)
canvas.backgroundColor = .clear
let iTest=UIImage(named: "Test2")
let imageView=UIImageView(image: iTest)
//This is what I have used for trying to resize my background image,
// although none of it seems to ever work
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.contentMode = .scaleAspectFit
view.addSubview(imageView)
view.sendSubviewToBack(imageView)
// since you already created the nameTextField as a lazy variable
// then you can add it here
view.addSubview(nameTextField)
view.bringSubviewToFront(nameTextField)
NSLayoutConstraint.activate([
canvas.leadingAnchor.constraint(equalTo: view.leadingAnchor),
canvas.trailingAnchor.constraint(equalTo: view.trailingAnchor),
canvas.topAnchor.constraint(equalTo: view.topAnchor),
canvas.bottomAnchor.constraint(equalTo: view.bottomAnchor)])
toolPicker.setVisible(true, forFirstResponder: canvas)
toolPicker.addObserver(canvas)
canvas.delegate = self
canvas.becomeFirstResponder()
if let drawing = try? PKDrawing(data: drawingData){
canvas.drawing = drawing
}
}
}
I need to move a method for adding and removing a logging view inside an Extension, in order to give it to every controller. to do so I added a inout UIVew parameter to original method, where I used a global var for the view. no I have this error
Value of type 'UIViewController' has no member 'containerForLoading'
removing self from self.containerForLoading will give error:
Escaping closure captures 'inout' parameter 'containerForLoading'
inside the animate closure (see the comment)
is all wrong the entire process or I am lost at the last step?
extension UIViewController {
func showLoadingView(containerForLoading: inout UIView, uponView: UIView) {
containerForLoading = UIView(frame: uponView.bounds)
uponView.addSubview(containerForLoading)
containerForLoading.backgroundColor = .white
containerForLoading.alpha = 0
UIView.animate(withDuration: 0.24) { self.containerForLoading.alpha = 0.8 } //here the error
let activivityIndicator = UIActivityIndicatorView()
containerForLoading.addSubview(activivityIndicator)
activivityIndicator.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
activivityIndicator.centerYAnchor.constraint(equalTo: uponView.centerYAnchor),
activivityIndicator.centerXAnchor.constraint(equalTo: uponView.centerXAnchor)
])
activivityIndicator.startAnimating()
}
func removeLoading(containerForLoading: inout UiView, uponView: UIView) {
containerForLoading.removeFromSuperview()
}
}
this is the code inside the original viewController
using this var
var containerForLoading = UIView()
called this way when needed
self.showLoadingView(uponView: self.view)
extension ViewController {
func showLoadingView(uponView: UIView) {
containerForLoading = UIView(frame: uponView.bounds)
uponView.addSubview(containerForLoading)
containerForLoading.backgroundColor = .white
containerForLoading.alpha = 0
UIView.animate(withDuration: 0.24) { self.containerForLoading.alpha = 0.8 }
let activivityIndicator = UIActivityIndicatorView()
containerForLoading.addSubview(activivityIndicator)
activivityIndicator.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
activivityIndicator.centerYAnchor.constraint(equalTo: uponView.centerYAnchor),
activivityIndicator.centerXAnchor.constraint(equalTo: uponView.centerXAnchor)
])
activivityIndicator.startAnimating()
}
func removeLoading(uponView: UIView) {
containerForLoading.removeFromSuperview()
}
}
You could make loadingContainerTag a local variable, and change the parameter name to something else. Then assign to the parameter just after you create the container view:
extension UIViewController {
func showLoadingView(containerForLoadingProperty: inout UIView, uponView: UIView) {
// local variable!
let containerForLoading = UIView(frame: uponView.bounds)
// also set property
containerForLoadingProperty = containerForLoading
uponView.addSubview(containerForLoading)
containerForLoading.backgroundColor = .white
containerForLoading.alpha = 0
// no "self."!
UIView.animate(withDuration: 0.24) { containerForLoading.alpha = 0.8 }
// ... everything else is the same
removeLoading could just have one non-inout parameter:
func removeLoading(containerForLoadingProperty: UIView) {
containerForLoadingProperty.removeFromSuperview()
}
But...
It is very weird for a method that shows a loading indicator, to need an inout parameter. It shouldn't assign to a special property that the caller provides. It should just show the loading indicator!
The purpose of your containerForLoading property is so that in removeLoading, you know which view to remove. If you don't store the containerForLoading view somewhere in a property, you wouldn't know which view to remove, right? Well, we can use the tag property of a view to identify views, so you can just make containerForLoading a local variable, and later in removeLoading, use its tag to find it.
extension UIViewController {
static let loadingContainerTag = <a number you like>
func showLoadingView(uponView: UIView) {
// local variable!
let containerForLoading = UIView(frame: uponView.bounds)
uponView.addSubview(containerForLoading)
containerForLoading.backgroundColor = .white
containerForLoading.alpha = 0
// set tag
containerForLoading.tag = UIViewController.loadingContainerTag
// no "self."!
UIView.animate(withDuration: 0.24) { containerForLoading.alpha = 0.8 }
let activivityIndicator = UIActivityIndicatorView()
containerForLoading.addSubview(activivityIndicator)
activivityIndicator.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
activivityIndicator.centerYAnchor.constraint(equalTo: uponView.centerYAnchor),
activivityIndicator.centerXAnchor.constraint(equalTo: uponView.centerXAnchor)
])
activivityIndicator.startAnimating()
}
func removeLoading(uponView: UIView) {
// find view with the tag
uponView.viewWithTag(UIViewController.loadingContainerTag)?.removeFromSuperview()
}
}
I want to show some custom control over vlc player,but when i am trying to do so, my control hides when actual video start running in player
player = VLCMediaPlayer()
player.media = VLCMedia(url: URL(string: "rtmp://сс.tv/sea")!)
player.drawable = view
let abc = uiview()
view.addsubview(abc)
I had tried to show uiview over my vlc player but failed to do so
player = VLCMediaPlayer()
player.media = VLCMedia(url: URL(string: "rtmp://сс.tv/sea")!)
player.drawable = view
I need to show some custom view whenever my video plays they should be visible , any help will pe appreciable
To add an overlay with custom controls you have to create a separate UIView which will hold the player, and create a controls UIView too. The first one should look something like this, with your own frame:
let root = UIView()
root.frame.size.height = UIScreen.main.bounds.width
root.frame.size.width = UIScreen.main.bounds.height
Now we can set "root" to be the main video rendering view for the player:
mediaPlayer.drawable = root
Create your custom controls view which will display on top of the player:
var child = UIHostingController(rootView: ControlsVLC())
child.view.frame.size.height = UIScreen.main.bounds.width
child.view.frame.size.width = UIScreen.main.bounds.height
child.view.backgroundColor = .clear // You will need to add this so you can see the video behind the controls
child.view.isOpaque = false
Lastly, you have to add them as subviews accordingly so the controls will display on top of the video:
self.addSubview(root)
self.addSubview(child.view)
self.bringSubviewToFront(child.view)
self.sendSubviewToBack(root) // Not sure if necessary but works
Be aware that this was done with SwiftUI on iOS 16.
Your final code should look something like below:
class PlayerUIView: UIView, VLCMediaPlayerDelegate {
var mediaPlayer = VLCMediaPlayer()
override init(frame: CGRect) {
super.init(frame: frame)
let root = UIView()
root.frame.size.height = UIScreen.main.bounds.width
root.frame.size.width = UIScreen.main.bounds.height
let url = URL(string: "your.source")!//replace your resource here
mediaPlayer.media = VLCMedia(url: url)
mediaPlayer.delegate = self
mediaPlayer.drawable = root
mediaPlayer.play()
var child = UIHostingController(rootView: ControlsVLC())
child.view.frame.size.height = UIScreen.main.bounds.width
child.view.frame.size.width = UIScreen.main.bounds.height
child.view.backgroundColor = .clear
child.view.isOpaque = false
self.addSubview(root)
self.addSubview(child.view)
self.bringSubviewToFront(child.view)
self.sendSubviewToBack(root)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
}
}
This worked for me.
Background:
I'm trying to animate a wave using BAFluidView. My goal is pretty simple: start the animation when a button is tapped, stop it when it is tapped again. The pod (linked above) provides all of the code to manage this.
Let it be known, I'm new to this. Help me learn, please!
My struggle:
I need to create a view for this wave. I put it into a function called "WaveContainer." Here's what that looks like:
func WaveAnimation() {
let wave = BAFluidView(frame: self.view.frame, startElevation: 0.02)!
wave.maxAmplitude = 10
wave.minAmplitude = 8
wave.fillDuration = 50
wave.fill(to: 0.95)
wave.fillAutoReverse = false
wave.fillColor = UIColor.blue
waveView.addSubview(wave)
}
I then called this in the ViewDidAppear function. Of course, it works! I can see the wave, and it's waving. Nice.
Of course, I can't call the wave constant anywhere else, though! If I want to stop / start the wave on a button press, for example?
If I try to move the wave constant out of this function into ViewDidLoad or ViewDidAppear, I can't access the ...self.view.frame, and the wave won't show up on the screen.
My asks:
How should I structure this code so that I can reference the wave constant from several different functions?
How should I reference the view for the wave constant when I'm not within ViewDidLoad or some other view-based function?
Thank you SO MUCH for your help!
My Code:
import UIKit
import BAFluidView
class ViewController: UIViewController {
// OUTLETS:
#IBOutlet weak var waveView: UIView!
// ACTIONS:
#IBAction func WaveButton(_ sender: UIButton) {
// If the user taps this button and the waveHasStarted variable is equal to false, flip it to true.
if waveHasStarted == false {
print("Start the wave with a press.")
startWave = true
waveHasStarted = true
waveHasBeenStopped = false
} else {
print("Stop the wave with a press.")
startWave = false
waveHasStarted = false
waveHasBeenStopped = true
}
}
// VARIABLES:
var waveHasStarted = false
var startWave = false
var waveHasBeenStopped = true
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {}
// FUNCTIONS:
func WaveAnimation() {
let wave = BAFluidView(frame: self.view.frame, startElevation: 0.02)!
wave.maxAmplitude = 10
wave.minAmplitude = 8
wave.fillDuration = 50
wave.fill(to: 0.95)
wave.fillAutoReverse = false
wave.fillColor = UIColor.blue
// If the variable above has been flipped to "true," start the animation...
if startWave == true {
print("Start that wave animation")
wave.startAnimation()
} else {
// If not, stop it or do nothing.
print("Stop that wave animation")
wave.stopAnimation()
wave.keepStationary()
}
waveView.addSubview(wave)
}
}
Use lazy initialization and get the class level access,
lazy var fluidView: BAFluidView = {
let wave = BAFluidView(frame: self.view.frame, startElevation: 0.02)!
wave.maxAmplitude = 10
wave.minAmplitude = 8
wave.fillDuration = 50
wave.fill(to: 0.95)
wave.fillAutoReverse = false
wave.fillColor = UIColor.blue
return wave
}()
Now you can add, start and stop the animation anywhere as,
waveView.addSubview(fluidView)
fluidView.startAnimation()
fluidView.stopAnimation()
I want to copy one UIView to another view without making it archive or unarchive.
Please help me if you have any solution.
I tried with by making an extension of UIView as already available an answer on Stack over flow. But its crashing when I pass the view with pattern Image Background color.
The code related to my comment below:
extension UIView
{
func copyView() -> UIView?
{
return NSKeyedUnarchiver.unarchiveObjectWithData(NSKeyedArchiver.archivedDataWithRootObject(self)) as? UIView
}
}
I've just tried this simple code in a Playground to check that the copy view works and it's not pointing the same view:
let originalView = UIView(frame: CGRectMake(0, 0, 100, 50));
originalView.backgroundColor = UIColor.redColor();
let originalLabel = UILabel(frame: originalView.frame);
originalLabel.text = "Hi";
originalLabel.backgroundColor = UIColor.whiteColor();
originalView.addSubview(originalLabel);
let copyView = originalView.copyView();
let copyLabel = copyView?.subviews[0] as! UILabel;
originalView.backgroundColor = UIColor.blackColor();
originalLabel.text = "Hola";
originalView.backgroundColor; // Returns black
originalLabel.text; // Returns "Hola"
copyView!.backgroundColor; // Returns red
copyLabel.text; // Returns "Hi"
If the extension wouldn't work, both copyView and originalView would have same backgroundColor and the same would happen to the text of the labels. So maybe there is the possibility that the problem is in other part.
Original Post
func copyView(viewforCopy: UIView) -> UIView {
viewforCopy.hidden = false //The copy not works if is hidden, just prevention
let viewCopy = viewforCopy.snapshotViewAfterScreenUpdates(true)
viewforCopy.hidden = true
return viewCopy
}
Updated for Swift 4
func copyView(viewforCopy: UIView) -> UIView {
viewforCopy.isHidden = false //The copy not works if is hidden, just prevention
let viewCopy = viewforCopy.snapshotView(afterScreenUpdates: true)
viewforCopy.isHidden = true
return viewCopy!
}