Changing background image with button - swift

I am trying to make an application function so that when the user presses one of the images(which are buttons) the background will change. My issue is that when the button is pressed the background will not change to the picture. I have been trying to look for answers but have found nothing. Is anyone able to help? https://i.stack.imgur.com/boEiZ.png <- The image is linked here because stack overflow won't allow me to imbed images.
#IBOutlet weak var background: NSImageView!
let grass = NSImage(named :NSImage.Name(rawValue: "wetgrass"))
override func viewDidLoad() {
super.viewDidLoad()
// Do view setup here.
background.image = grass
}
#IBAction func changeGrass(_ sender: Any) {
background.image = grass
}
}

Here is a tested answer try this
#IBAction func btnChangeImageClick(_ sender: UIButton) {
let imageFromButton : UIImage = sender.image(for: UIControlState.normal)!
imgBG.image = imageFromButton
}
Note: i make Any to UIButton in action so we can get image from that sender object

How to approach solving the problem:
Find out if the assignment upon button press is the problem; isolate image-setting from the button callbacks. You did that already by assigning an image in viewDidLoad ✓
Find out if the image is broken.
The image loading could fail:
let grass = NSImage(named: NSImage.Name(rawValue: "wetgrass"))
NSImage.init?(named:) produces a NSImage? aka Optional<NSImage> -- that means, loading the image file with that name could fail. Use this initializer when you're positive that ...
either your Bundle contains an image file named "wetgrass.png" that is copies into the app (check Build Settings > Copy Bundle Resources),
or your Assets asset catalogue contains an entry with the name "wetgrass".
So grass could be nil. And if it is nil, your assignment to background.image will simply remove any previously set image (if there ever was any), resetting the NSImageView to display a boring gray background.
To finally isolate and check if image assignment works, use an image that's guaranteed to exist, like the built-in ones:
let testImage = NSImage(named: .advanced)!
Note I force-unwrap the optional here with confidence since NSImage.Name.advanced is guaranteed to produce an image :)
This should work then:
override func viewDidLoad() {
super.viewDidLoad()
// Show initial image:
background.image = NSImage(named: .advanced)!
}
#IBAction func changeGrass(_ sender: Any) {
// Show grass or another built-in image:
background.image = grass ?? NSImage(named: .computer)!
}
So check your grass != nil

Try this:
#IBAction func changeGrass(_ sender: Any) {
if let myButton = sender as? UIButton, let currentImage = myButton.imageView?.image {
background.image = currentImage
// Animated
UIView.transition(with: background,
duration: 0.75,
options: .transitionCrossDissolve,
animations: {
self.background.image = currentImage
})
}
}

Related

How would I make a UiPanGestureRecognizer check if the finger is in the buttons frame?

I am trying to make an app where the user could drag a finger on top of multiple buttons and get some actions for each button.
There was a similar question from a while back but when I tried to use CGRectContainsPoint(button.frame, point) it said it was replaced with button.frame.contains(point) but this didn’t seem to work for me. Here is a link to the Photo
What I have done so far:
var buttonArray:NSMutableArray!
override func viewDidLoad()
{
super.viewDidLoad()
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(panGestureMethod(_:)))
a1.addGestureRecognizer(panGesture)
a2.addGestureRecognizer(panGesture)
}
#objc func panGestureMethod(_ gesture: UIPanGestureRecognizer) {
if gesture.state == UIGestureRecognizer.State.began {
buttonArray = NSMutableArray()
}
let pointInView = gesture.location(in: gesture.view)
if !buttonArray.contains(a1!) && a1.frame.contains(pointInView) {
buttonArray.add(a1!)
a1Tapped(a1)
}
else if !buttonArray.contains(a2!) && a2.frame.contains(pointInView) {
buttonArray.add(a2!)
a2Tapped(a2)
}
The code did run fine but when I tried to activate the drag nothing happened. Any tips?
You want behavior similar to the system keyboard I assume? CGRectContainsPoint is not deprecated: See the docs. In Swift it's written like frame.contains().
When dealing with rects and points you have to make sure both are translated to the same coordinate system first. To do so you can use the convert to/from methods on UIView: See (the docs).
In your case smth. like the following should work (first translate button frames, then check if the point is inside):
func touchedButtonForGestureRecognizer(_ gesture: UIPanGestureRecognizer) -> UIView? {
let pointInView = gesture.location(in: gesture.view)
for (button in buttonArray) {
let rect = gesture.view.convert(button.frame from:button.superview)
if (rect.contains(pointInView)) {
return button
}
}
return nil
}

HitTest prints AR Entity name even when I am not tapping on it

My Experience.rcproject has animations that can be triggered by tap action.
Two cylinders are named “Button 1” and “Button 2” and have Collide turned on.
I am using Async method to load Experience.Map scene and addAnchor method to add mapAnchor to ARView in a ViewController.
I tried to run HitTest on the scene to see if the app reacts properly.
Nonetheless, the HitTest result prints the entity name of a button even when I am not tapping on it but area near it.
class augmentedReality: UIViewController {
#IBOutlet weak var arView: ARView!
#IBAction func onTap(_ sender: UITapGestureRecognizer) {
let tapLocation = sender.location(in: arView)
// Get the entity at the location we've tapped, if one exists
if let button = arView.entity(at: tapLocation) {
// For testing purposes, print the name of the tapped entity
print(button.name)
}
}
}
Below is my attempt to add the AR scene and tap gesture recogniser to arView.
class augmentedReality: UIViewController {
arView.scene.addAnchor(mapAnchor)
mapAnchor.notifications.hideAll.post()
mapAnchor.notifications.mapStart.post()
self.arView.isUserInteractionEnabled = true
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(onTap))
self.arView.addGestureRecognizer(tapGesture)
}
Question 1
How can I achieve the goal of only having the entity name of a button printed when I am really tapping on it instead of close to it?
Question 2
Do I actually need to turn Collide on to have both buttons able to be detected in the HitTest?
Question 3
There’s an installGestures method. There’s no online tutorials or discussions about this at the moment. I tried but I am confused by (Entity & HasCollision). How can this method be implemented?
To implement a robust Hit-Testing in RealityKit, all you need is the following code:
import RealityKit
class ViewController: UIViewController {
#IBOutlet var arView: ARView!
let scene = try! Experience.loadScene()
#IBAction func onTap(_ sender: UITapGestureRecognizer) {
let tapLocation: CGPoint = sender.location(in: arView)
let result: [CollisionCastHit] = arView.hitTest(tapLocation)
guard let hitTest: CollisionCastHit = result.first
else { return }
let entity: Entity = hitTest.entity
print(entity.name)
}
override func viewDidLoad() {
super.viewDidLoad()
scene.steelBox!.scale = [2,2,2]
scene.steelCylinder!.scale = [2,2,2]
scene.steelBox!.name = "BOX"
scene.steelCylinder!.name = "CYLINDER"
arView.scene.anchors.append(scene)
}
}
When you tap on entities in ARView a Debug Area prints "BOX" or "CYLINDER". And if you tap anything but entities, a Debug Area prints just "Ground Plane".
If you need to implement a Ray-Casting read this post, please.
P.S.
In case you run this app on macOS Simulator, it prints just Ground Plane instead of BOX and CYLINDER. So you need to run this app on iPhone.

How to make image change for a few seconds when user clicks button and then revert back to original image in swift?

Hi I am new to coding with Swift and I am also pretty new to Stackoverflow. How do I make a button that changes the image for about 5 seconds and then go back to the original image when the user taps on the button?
I've tried using this code
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (Int64)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), {
gauntletImage.image = UIImage(named: gauntlet["gauntlet2"]) //change back to the old image after 5 sec
});
But I keep getting these 2 errors:
Cannot convert value of type 'Int' to expected argument type 'dispatch_time_t' (aka 'UInt64')
and
Ambiguous use of 'dispatch_get_main_queue()'
This is more of my code that I am using.
#IBOutlet weak var gauntletImage: UIImageView!
let gauntlet = ["gauntlet1", "gauntlet2", "gauntlet3", "gauntlet4", "gauntlet5", "gauntlet6",]
#IBAction func stonePressed(_ sender: UIButton) {
print(sender.tag)
gauntletImage.image = UIImage(named: gauntlet[sender.tag - 1]) //change to the new image
dispatch_after(dispatch_time(dispatch_time_t(DISPATCH_TIME_NOW), (Int64)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), {
gauntletImage.image = UIImage(named: gauntlet["gauntlet2"]) //change back to the old image after 5 sec
});
}
You can try
#IBAction func stonePressed(_ sender: UIButton) {
// store old image assuming it has an initial image in storyboard
let oldImg = gauntletImage.image!
// set new image
gauntletImage.image = UIImage(named: gauntlet[sender.tag - 1])
// wait 5 seconds
DispatchQueue.main.asyncAfter(deadline: .now() + 5 ) {
// set back old image
self.gauntletImage.image = oldImg
}
}
User may click it multiple times so you may do
sender.isEnabled = false
when storing the old image and setting it back to true inside the dispatch after block

gesture recognizer issue with tic tac toe in xcode

hello there i am trying to figure out what the issue is why i cant get a cross on the game board when a tap is made. the TTT class is the one below the first code and it is called in the main code. i will appreciate if some can help me on this
#IBOutlet var fields: [TTTImageView]!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
func fieldTapped (recognizer:UITapGestureRecognizer) {
let tappedField = recognizer.view as! TTTImageView
tappedField.setPlayer(_player: "x.png")
}
func setupField () {
for index in 0 ... fields.count - 1 {
let gestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(fieldTapped))
gestureRecognizer.numberOfTapsRequired = 1
fields[index].addGestureRecognizer(gestureRecognizer)
}
}
import UIKit
class TTTImageView: UIImageView {
var player:String?
var activated:Bool! = false
func setPlayer (_player:String) {
self.player = _player
if activated == false {
if _player == "x.png" {
self.image = UIImage (named: "x.png")
}else{
self.image = UIImage (named: "o.png")
}
activated = true
}
}
}
Make sure when you set fields[index].addGestureRecognizer(gestureRecognizer) to set isUserInteractionEnabled to true.
fields[index].isUserInteractionEnabled = true
Whenever you want to detect user activity (e.g. UITapGestureRecognizer on a UIImageView, you need to set this to true
EDIT:
I also wanted to mention something I noticed after looking at your code again: the method you are using for selectors has been deprecated. If you are using Swift 3 (which I highly recommend you do), you shouldn't call selectors like this: "functionName:" anymore. Now we use selectors like so: #selector(functionName(_:)). You should start updating your code to the most current syntax sooner rather than later.

How to use a if else statement on constraints in swift

The code below has a uicolor box when pressed, the box becomes a smaller uicolor box. The problem is that the color box can only be pressed one time. So the box can go from large to small but it cant go from small to big. How can the code be written so when the box is clicked the box goes from big to small or small to big every time.
import UIKit
class ViewController: UIViewController {
let colorview = UIView()
var initialc = [NSLayoutConstraint]()
override func viewDidLoad() {
super.viewDidLoad()
let tapGestureRecognizer = UITapGestureRecognizer(target:self, action:#selector(ViewController.myFunction(_:)))
colorview.userInteractionEnabled = true
colorview.addGestureRecognizer(tapGestureRecognizer)
colorview.translatesAutoresizingMaskIntoConstraints = false
colorview.backgroundColor = UIColor.blueColor()
self.view.addSubview((colorview))
let leadingc = colorview.leadingAnchor.constraintEqualToAnchor(self.view.leadingAnchor)
let trailingC = colorview.trailingAnchor.constraintEqualToAnchor(self.view.trailingAnchor)
let topc = colorview.topAnchor.constraintEqualToAnchor(self.view.topAnchor)
let bottomc = colorview.bottomAnchor.constraintEqualToAnchor(self.view.bottomAnchor, constant: -50)
initialc.appendContentsOf([leadingc,trailingC,topc,bottomc])
NSLayoutConstraint.activateConstraints(initialc)
}
func myFunction(sender: AnyObject) {
NSLayoutConstraint.deactivateConstraints(initialc)
let widthc = colorview.widthAnchor.constraintEqualToConstant(100)
let heightc = colorview.heightAnchor.constraintEqualToConstant(100)
let centerxc = colorview.centerXAnchor.constraintEqualToAnchor(self.view.centerXAnchor)
let centeryc = colorview.centerYAnchor.constraintEqualToAnchor(self.view.centerYAnchor)
NSLayoutConstraint.activateConstraints([widthc,heightc,centerxc,centeryc])
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
You are resizing the view in an awkward way. Instead of using constraints, don't place them. Tell the view where to go with its frame. If you need the view to animate the change between frames then use UIView.animateViewWithDuration() and inside the function do the frame changes you desire. This keeps transitions smooth and full control in your hands. Constraints are not as easy to implement properly during run time as frames.