Issue with using variables inside a class function (Swift Playgrounds) - swift

I want to create a function so when a button gets pressed, the content of a variable changes. The function does get called, but I can't use any external variables inside it.
This is the code I'm having trouble with:
class Responder: NSObject {
func pressed(sender: UIButton) {
// Can't change the value of "a" here
}
}
var a: Int = 0
let responder = Responder()
let bt = UIButton(frame: CGRect(x: 0.0, y: 0.0, width: 200.0, height: 200.0))
bt.addTarget(responder, action: #selector(Responder.pressed), for: .touchUpInside)
What can I do to make this work?

It does work. You can change the value of a in pressed. I added code to your pressed method, as follows:
import UIKit
import PlaygroundSupport
class Responder: NSObject {
func pressed(sender: UIButton) {
a = a+1
print(a)
}
}
var a: Int = 0
let responder = Responder()
let bt = UIButton(frame: CGRect(x: 0.0, y: 0.0, width: 200.0, height: 200.0))
bt.addTarget(responder, action: #selector(Responder.pressed), for: .touchUpInside)
PlaygroundPage.current.liveView = bt
The button appears, and each time I tap it, a higher number is printed out, starting with 1 (because a was 0 to start with). This proves that your code can change a.

Related

How to get property of tapped button mapped from array?

I am trying to print properties from an array containing properties of a class when tapping on that button, I have read some answers but none are working. apparently println() use to work but it seems to have been depreciated. Below is the code in question:
this is the buttons class
class Buttons{
var nature = UIButton(type: UIButton.ButtonType.custom)
var name: String
init ( name: String) {
self.name = name
}
}
this is the array in the main class
var buttonsArray=[
Buttons(name: "below")
,
Buttons(name: "front")
,
Buttons(name: "right")
]
mapping:
for val in buttonsArray{
val.nature.frame = CGRect(x: 2, y: 10, width: 20, height:20)
val.nature.layer.cornerRadius = 0.5 * 0.12*bounds.size.width
val.nature.clipsToBounds = true
val.nature.backgroundColor = .blue
sceneView.addSubview(val.nature)
val.nature.adjustsImageWhenHighlighted = false
val.nature.addTarget(self, action: #selector(profileButtonClicked), for: UIControl.Event.touchDown)
val.nature.addTarget(self, action: #selector(profileButtonOut), for: UIControl.Event.touchUpInside)
val.nature.addTarget(self, action: #selector(profileButtonOut), for: UIControl.Event.touchDragExit)
}
assigned func
#objc func profileButtonClicked(button: self){
print(button.name)
}
I get an error in the function thought but the point is to print the tapped button name.
Thanks for your time
You can change the function parameter like
#objc func profileButtonClicked(button: Button){ print(button.name) }
For reference, you can see
[1]: What is "self" used for in Swift?
You also mentioned you are getting error. It would be better if you share the error message.
Since you are getting bad access. In that case I have two solutions for you, I tested both and worked well.
First one, you need to modify your profileButtonClicked() function like below
#objc func profileButtonClicked(_ button: Buttons){
for x in buttonsArray {
if x.nature == button {
print(x.name)
break
}
}
}
Or I prefer this one,
Main class change would be,
var buttonList = [
ButtonTest(name: "below"),
ButtonTest(name: "front"),
ButtonTest(name: "right")]
for button in buttonList {
sceneView.addSubview(button)
}
You can replace Button class with ButtonTest,
class ButtonTest: UIButton {
var name: String
init(name: String){
self.name = name
super.init(frame:.zero)
self.frame = CGRect(x: 2, y: 10, width: 100, height:40)
self.layer.cornerRadius = 0.5 * 0.12 * 200
self.clipsToBounds = true
self.backgroundColor = .blue
//self.setTitle(name, for: .normal)
self.adjustsImageWhenHighlighted = false
//self.contentEdgeInsets = UIEdgeInsets(top: 10, left: 10, bottom: 0, right: -25.0)
//view.addSubview(val.nature)
self.addTarget(self, action: #selector(profileButtonClicked), for: UIControl.Event.touchDown)
//
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
#objc func profileButtonClicked(button: ButtonTest){
print(button.name)
}
}

How to remove programmatically created UIViews from superview

I wanted to implement a loading overlay whilst I have content loading in from an API call however when I go to dismiss the view; I have no success.
func viewLoading(show:Bool, boxView: UIView, error: Bool, errorMessage: String){
let myNewView=UIView(frame: CGRect(x: 0, y: 0, width: boxView.frame.width, height: boxView.frame.height))
if show{
// Change UIView background colour
myNewView.backgroundColor = UIColor.black.withAlphaComponent(0.75)
myNewView.isOpaque = false
// Add rounded corners to UIView
myNewView.layer.cornerRadius = boxView.layer.cornerRadius
let activityView = UIActivityIndicatorView(style: .whiteLarge)
activityView.center = myNewView.center
activityView.startAnimating()
boxView.addSubview(myNewView)
myNewView.addSubview(activityView)
}else{
print("Done")
DispatchQueue.main.async(execute: { () -> Void in
myNewView.removeFromSuperview()
self.view.bringSubviewToFront(boxView)
})
myNewView.isHidden = true
}
}
None of the options after else have worked and I am lost at a solution.
Edit: I want the same function(s) to accommodate three different views within the one view controller.
Move myNewView outside of the viewLoading function scope, and it is better to create separate methods with their own responsibilities, like so:
class ViewController: UIViewController {
var loaderView: UIView?
func showLoading(boxView: UIView, error: Bool, errorMessage: String) {
if (self.loaderView != nil) {
self.hideLoading()
}
let newView = UIView(frame: CGRect(x: 0, y: 0, width: boxView.frame.width, height: boxView.frame.height))
newView.backgroundColor = UIColor.black.withAlphaComponent(0.75)
newView.isOpaque = false
// Add rounded corners to UIView
newView.layer.cornerRadius = boxView.layer.cornerRadius
let activityView = UIActivityIndicatorView(style: .whiteLarge)
activityView.center = newView.center
activityView.startAnimating()
boxView.addSubview(newView)
newView.addSubview(activityView)
self.loaderView = newView
}
func hideLoading() {
guard
let loaderView = self.loaderView,
let boxView = loaderView.superview
else { return }
DispatchQueue.main.async {
loaderView.removeFromSuperview()
self.view.bringSubviewToFront(boxView) // need this?
self.loaderView = nil
}
}
}
You are creating a new view every time that method is called and then you are trying to dismish that newly created view. Instead, you should save a reference of the view when you show it and call removeFromSuperview on that instance when you need to hide it.
Check this..
CommonMethods.swift
import UIKit
class CommonMethods: UIViewController {
static let actInd: UIActivityIndicatorView = UIActivityIndicatorView()
static let container: UIView = UIView()
static let loadingView: UIView = UIView()
static func showActivityIndicatory(uiView: UIView) {
container.frame = uiView.frame
container.center = uiView.center
container.backgroundColor = UIColor(red:255/255, green:255/255, blue:255/255, alpha: 0.3)
loadingView.frame = CGRect(origin: CGPoint(x: 0,y :0), size: CGSize(width: 80, height: 80))
loadingView.center = uiView.center
loadingView.backgroundColor = UIColor(red:44/255, green:44/255, blue:44/255, alpha: 0.7)
loadingView.clipsToBounds = true
loadingView.layer.cornerRadius = 10
actInd.frame = CGRect(origin: CGPoint(x: 0,y :0), size: CGSize(width: 40, height: 40))
actInd.style =
UIActivityIndicatorView.Style.whiteLarge
actInd.center = CGPoint(x: loadingView.frame.size.width / 2, y: loadingView.frame.size.height / 2);
loadingView.addSubview(actInd)
container.addSubview(loadingView)
uiView.addSubview(container)
actInd.startAnimating()
}
static func hideActivityIndicatory(uiView: UIView) {
container.removeFromSuperview()
actInd.stopAnimating()
}
}
call it from viewcontroller class like
CommonMethods.showActivityIndicatory(uiView: self.view)
CommonMethods.hideActivityIndicatory(uiView: self.view)

I am trying to make a helper class for admobs in my app

I have a Admobs ADHelper class in my app. The Code for this class is:
import UIKit
import Firebase
import GoogleMobileAds
class AdHelper {
static let shared = AdHelper()
func initAdMobBannerView(inViewController viewController: UIViewController) -> GADBannerView {
var adMobBannerView = GADBannerView()
if UIDevice.current.userInterfaceIdiom == .phone {
// iPhone
adMobBannerView.adSize = GADAdSizeFromCGSize(CGSize(width: 320, height: 50))
adMobBannerView.frame = CGRect(x: 0, y: viewController.view.frame.size.height, width: 320, height: 50)
} else {
// iPad
adMobBannerView.adSize = GADAdSizeFromCGSize(CGSize(width: 468, height: 60))
adMobBannerView.frame = CGRect(x: 0, y: viewController.view.frame.size.height, width: 468, height: 60)
}
adMobBannerView.adUnitID = "ca-app-pub-2722404425168911/4464296386"
adMobBannerView.rootViewController = viewController
adMobBannerView.delegate = viewController as? GADBannerViewDelegate
//viewController.addSubview(adMobBannerView)
let request = GADRequest()
adMobBannerView.load(request)
return adMobBannerView
UIView.beginAnimations("showBanner", context: nil)
UIView.commitAnimations()
adMobBannerView.isHidden = false
}
}
and I am trying to call this class from my another view controller:
func playAd() {
let adMobBannerView = AdHelper.shared.initAdMobBannerView(inViewController: self)
view.addSubview(adMobBannerView)
}
I call this function in viewdidload as playAd()
No ads are still loading up!
Take a look at the definition of your class:
class AdHelper: UIViewController
Why does it have to extend UIViewController? It is a helper class, so it shouldn't have to extend UIViewController.
You say:
The problem is ads are not showing up at all when I do this but all of this code is put in the same view controller (without using shared class), they show up.
Take a look at your initAdMobBanner method, this line for instance:
adMobBannerView.frame = CGRect(x: 0, y: view.frame.size.height, width: 320, height: 50)
You're setting the frame of your adMobBannerView to start at the height of view.frame...but the questions are....how large is view? Where is it initialised?
And later on:
view.addSubview(adMobBannerView)
Here you are adding your adMobBannerView to view, but what is the size of view?
This also explains:
but all of this code is put in the same view controller (without using shared class), they show up
Yes...because here you know what size the view is, and you can add content to it.
What I'm hinting at here is, that I think you should create a separate class that does not inherit from UIViewController and then have a method that takes a UIViewController as parameter and uses that to create a new adMobBannerView.
Something like:
class AdHelper {
static let shared = AdHelper()
func initAdMobBannerView(inViewController viewController: UIViewController) -> GADBannerView {
var adMobBannerView = GADBannerView()
if UIDevice.current.userInterfaceIdiom == .phone {
// iPhone
adMobBannerView.adSize = GADAdSizeFromCGSize(CGSize(width: 320, height: 50))
adMobBannerView.frame = CGRect(x: 0, y: viewController.view.frame.size.height, width: 320, height: 50)
} else {
// iPad
adMobBannerView.adSize = GADAdSizeFromCGSize(CGSize(width: 468, height: 60))
adMobBannerView.frame = CGRect(x: 0, y: viewController.view.frame.size.height, width: 468, height: 60)
}
adMobBannerView.adUnitID = ADMOB_BANNER_UNIT_ID
adMobBannerView.rootViewController = viewController
return adMobBannerView
}
}
Which your could then use in your normal ViewControllers like so:
func playAd() {
let adMobBannerView = AdHelper.shared.initAdMobBannerView(inViewController: self)
view.addSubview(adMobBannerView)
let request = GADRequest()
adMobBannerView.load(request)
}
Hope that makes sense.

How to add a UIButton to Swift Playground?

So I opened up playground, I just want to add a simple UIButton (or a simple UIView) for testing purposes. I can't get it to display though. This is what I have so far:
import UIKit
var uiButton = UIButton.buttonWithType(UIButtonType.System) as UIButton
uiButton.frame = CGRectMake(0, 0, 100, 100)
uiButton.setTitle("Test", forState: UIControlState.Normal);
//self.view.addSubview(uiButton) <-- doesn't work
Do you guys know what I'm doing wrong? Thanks
I think you can add button into playground and your code is correct you can see your button here when you click on Quick Look:
or you can see that button with clicking on Value History:
you dont need to add that into view.
If you import XCPlayground in your playground you can show views using:
let view=UIView()
//other setup
XCPShowView("View Title",view)
This will show the view in the assistant editor
To show the assistant editor goto View > Assistant Editor > Show Assistant Editor
import XCPlayground has been deprecated and is now import PlaygroundSupport.
You need to create your view first and give it a size.
let view = UIView(frame: CGRect(x: 0, y: 0, width: 300, height: 300))
view.backgroundColor = UIColor.black
then add PlaygroundPage.current.liveView = view to be able to see the view in the assistant editor.
now that you have created your view, and you can see the live preview of it. You can now add other views to it. In my playground I added several views so I created simple function with a default width and height.
func addSquareView(x: Int, y: Int, width: Int = 100, height: Int = 100, color: UIColor) {
let newView = UIView(frame: CGRect(x: x, y: y, width: width, height: height))
newView.backgroundColor = color
view.addSubview(newView)
}
addSquareView(x: 100, y: 100, color: .blue)
Now there is a blue square in the center of my view.
I was just doing my sorting programming and pated the code..it will help you to add a button in playgroud with target.
import UIKit
import PlaygroundSupport
let aButton = UIButton(frame: CGRect(x: 0, y: 0, width: 100, height: 50))
aButton.backgroundColor = .lightGray
aButton.setTitle("Run Sorting", for: .normal)
aButton.layer.cornerRadius = 10
aButton.clipsToBounds = true
class Sorting {
#objc func callSorting() {
self.getSortedArrayUsingSelectionSort()
}
func getSortedArrayUsingSelectionSort() {
var arr = [4, 9, 10, 6, 1, 3]
let n = arr.count
for i in 0..<(n - 1) {
var minIndex = i
for j in (i+1)..<n {
if arr[j] < arr[minIndex] {
minIndex = j
}
}
if minIndex != i {
let temp = arr[minIndex]
arr[minIndex] = arr[i]
arr[i] = temp
} }
print(arr)
}
}
let target = Sorting()
aButton.addTarget(target, action: #selector(Sorting.callSorting), for: .touchUpInside)
let containerView = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
containerView.backgroundColor = .white
PlaygroundPage.current.liveView = containerView
containerView.addSubview(aButton)

Select all text in a NSTextField using Swift

How can I programatically select all the text in a NSTextField using Swift?
For UITextField theres a method like
textField.selectedTextRange = textField.textRangeFromPosition(...)
Try this in a playground:
import XCPlayground
import AppKit
let view = NSView(frame: CGRect(x: 0, y: 0, width: 300, height: 300))
XCPShowView("view", view)
let txtf = NSTextField(frame: CGRect(x: 50, y: 50, width: 200, height: 50))
view.addSubview(txtf)
txtf.stringValue = "falling asleep at the keyboard..."
txtf.selectText(nil) // Ends editing and selects the entire contents of the text field
var txt = txtf.currentEditor() // returns an NSText
txt.selectedRange // --> (0,33)
Command-click on selectedRange, then on NSText (its return type), which will jump you to its Swiftened header where you can check out its rich functionality...
Better solution:
import Cocoa
class TextFieldSubclass: NSTextField {
override func mouseDown(theEvent: NSEvent) {
super.mouseDown(theEvent)
if let textEditor = currentEditor() {
textEditor.selectAll(self)
}
}
}
Or for precise selection:
import Cocoa
class TextFieldSubclass: NSTextField {
override func mouseDown(theEvent: NSEvent) {
super.mouseDown(theEvent)
if let textEditor = currentEditor() {
textEditor.selectedRange = NSMakeRange(location, length)
}
}
}
Works for me:
import Cocoa
class TextFieldSubclass: NSTextField {
override func becomeFirstResponder() -> Bool {
let source = CGEventSourceCreate(CGEventSourceStateID.HIDSystemState)
let tapLocation = CGEventTapLocation.CGHIDEventTap
let cmdA = CGEventCreateKeyboardEvent(source, 0x00, true)
CGEventSetFlags(cmdA, CGEventFlags.MaskCommand)
CGEventPost(tapLocation, cmdA)
return true
}
}