How to make this UIButton's action operate correctly? - swift

Every time I run this swift playground (on Xcode), I get an error on the last line saying:
'PlaygroundView' cannot be constructed because it has no accessible initialisers.
I also have another error on line 4 saying:
class 'PlaygroundView' has no initializers.
import UIKit
import PlaygroundSupport
class PlaygroundView:UIViewController {
var introImage:UIImageView
var introButton:UIButton
override func loadView() {
print("workspace started running.")
//Main Constant and Variable Declarations/General Setup
let mainView = UIView()
//Main View Modifications & styling
mainView.backgroundColor = UIColor.init(colorLiteralRed: 250, green: 250, blue: 250, alpha: 1)
func addItem(item: UIView) {
mainView.addSubview(item)
}
//Sub-Element Constant and Variable Declarations
introImage = UIImageView(frame: CGRect(x: 87.5, y: -300, width: 200, height: 200))
introImage.layer.borderColor = UIColor.black.cgColor
introImage.layer.borderWidth = 4
introImage.alpha = 0
introImage.transform = CGAffineTransform(rotationAngle: -90.0 * 3.14/180.0)
introImage.image = UIImage(named: "profile_pic.png")
introImage.image?.accessibilityFrame = introImage.frame
introImage.layer.cornerRadius = 100
addItem(item: introImage)
introButton = UIButton(frame: CGRect(x: 87.5, y: 900, width: 200, height: 30))
introButton.alpha = 0
introButton.setTitle("Tap to meet me", for: .normal)
introButton.titleLabel?.font = UIFont(name: "Avenir Book", size: 20)
introButton.backgroundColor = UIColor.black
introButton.layer.cornerRadius = 15
introButton.layer.borderWidth = 5
addItem(item: introButton)
introButton.addTarget(self, action: #selector(self.introButtonAction), for: .touchDown)
UIView.animate(withDuration: 1.5, delay: 1, options: .curveEaseInOut, animations: {
self.introImage.frame = CGRect(x: 87.5, y: 175, width: 200, height: 200)
self.introButton.frame = CGRect(x: 87.5, y: 400, width: 200, height: 30)
self.introImage.transform = CGAffineTransform(rotationAngle: 0.0 * 3.14/180.0)
self.introImage.alpha = 1
self.introButton.alpha = 1
}) { (mainAnimationComped) in
if mainAnimationComped == true {
print("introduction animation comped.")
}
}
}
//User Interface Actions
func introButtonAction() {
print("user interacted with user interface")
}
}
PlaygroundPage.current.liveView = PlaygroundView()
How would I fix this problem?

That's because in Swift, you need to initialize variables before use them in the code. On the other hand, since in this case you set them explicitly in your function, you might declare them as implicitly unwrapped optionals
var introImage:UIImageView!
var introButton:UIButton!
and this should work without changing anything else in your code

Add ? after introImage and introButton
var introImage:UIImageView?
var introButton:UIButton?

Related

issue with animation in swift playground

I'm totally new to animations with swift and I started this concept by using playground here's my code below I have no "idea" why it doesn't work.
import UIKit
import PlaygroundSupport
let liveViewFrame = CGRect(x: 0, y: 0, width: 500, height: 500)
let liveView = UIView(frame: liveViewFrame)
liveView.backgroundColor = .white
PlaygroundPage.current.liveView = liveView
let smallFrame = CGRect(x: 0, y: 0, width: 100, height: 100)
let square = UIView(frame: smallFrame)
square.backgroundColor = .purple
liveView.addSubview(square)
UIView.animate(withDuration: 3.0) {
liveView.backgroundColor = .blue
}
This piece of code is working fine

Toast UILabel appears off screen, despite me setting y correctly

I have a Toast extension on a ViewController that inherits class ViewControllerList: UITableViewController, UIPopoverPresentationControllerDelegate { and everything is fine except that when I do this
extension UIViewController {
func showToast(message : String) {
let height = UIScreen.main.bounds.height
let toastLabel = UILabel(frame: CGRect(x: self.view.frame.size.width/2 - 75, y: height-100, width: 150, height: 35))
toastLabel.backgroundColor = UIColor.black.withAlphaComponent(0.6)
toastLabel.textColor = UIColor.white
toastLabel.textAlignment = .center;
toastLabel.font = UIFont(name: "Montserrat-Light", size: 12.0)
toastLabel.text = message
toastLabel.alpha = 1.0
toastLabel.layer.cornerRadius = 10;
toastLabel.clipsToBounds = true
self.view.addSubview(toastLabel)
UIView.animate(withDuration: 4.0, delay: 0.1, options: .curveEaseOut, animations: {
toastLabel.alpha = 0.0
}, completion: {(isCompleted) in
toastLabel.removeFromSuperview()
})
}
}
the toast ALWAYS appears off screen .. in a long uitableview .. no matter how I set the y it always thinks in terms of document.. I have debugged it and y=676 yet it appears about placement at around ~900 ...100 off the bottom of the tableView just inside the last cell
why is this and how do I fix it?
Please do not mark this down - TRY to give an answer
Instead of adding to self.view, add your toast label as a subview of the window:
extension UIViewController {
func showToast(message : String) {
let window = (UIApplication.shared.delegate as! AppDelegate).window
let height = window.bounds.height
let toastLabel = UILabel(frame: CGRect(x: window.bounds.width/2 - 75, y: height-100, width: 150, height: 35))
toastLabel.backgroundColor = UIColor.black.withAlphaComponent(0.6)
toastLabel.textColor = UIColor.white
toastLabel.textAlignment = .center;
toastLabel.font = UIFont(name: "Montserrat-Light", size: 12.0)
toastLabel.text = message
toastLabel.alpha = 1.0
toastLabel.layer.cornerRadius = 10;
toastLabel.clipsToBounds = true
// notice the change here
window.addSubview(toastLabel)
UIView.animate(withDuration: 4.0, delay: 0.1, options: .curveEaseOut, animations: {
toastLabel.alpha = 0.0
}, completion: {(isCompleted) in
toastLabel.removeFromSuperview()
})
}
}
This avoids the problems you have to deal with when the super view is a scroll view.
Add a new function addToTopView(view : UIView) and use like here.
extension UIViewController {
/// adding views as a subview of the top view = window
func addToTopView(view : UIView) {
if let d = UIApplication.shared.delegate, let window = d.window! {
window.addSubview(view)
// handle constraints or anythings
}
}
func showToast(message : String) {
let height = UIScreen.main.bounds.height
let toastLabel = UILabel(frame: CGRect(x: self.view.frame.size.width/2 - 75, y: height-100, width: 150, height: 35))
toastLabel.backgroundColor = UIColor.black.withAlphaComponent(0.6)
toastLabel.textColor = UIColor.white
toastLabel.textAlignment = .center;
toastLabel.font = UIFont(name: "Montserrat-Light", size: 12.0)
toastLabel.text = message
toastLabel.alpha = 1.0
toastLabel.layer.cornerRadius = 10;
toastLabel.clipsToBounds = true
addToTopView(view: toastLabel) // here is update
UIView.animate(withDuration: 4.0, delay: 0.1, options: .curveEaseOut, animations: {
toastLabel.alpha = 0.0
}, completion: {(isCompleted) in
toastLabel.removeFromSuperview()
})
}
}

Multiple NSTextField in an NSAlert - MacOS

I am putting together a login dialog using an NSAlert in Swift 4 on MacOS. Here is the code I have so far:
func dialogOKCancel(question: String, text: String) -> (String, String) {
let alert = NSAlert()
alert.messageText = question
alert.informativeText = text
alert.alertStyle = .warning
alert.addButton(withTitle: "Login")
alert.addButton(withTitle: "Cancel")
let unameField = NSTextField(frame: NSRect(x: 0, y: 0, width: 200, height: 24))
let passField = NSSecureTextField(frame: NSRect(x: 0, y: 0, width: 200, height: 24))
let stackViewer = NSStackView()
stackViewer.addSubview(unameField)
alert.accessoryView = stackViewer
let response: NSApplication.ModalResponse = alert.runModal()
if (response == NSApplication.ModalResponse.alertFirstButtonReturn) {
return (unameField.stringValue, passField.stringValue)
} else {
return ("", "")
}
}
This works just fine to show either the unameField or passField when I do alert.accessoryView = passField. But I want to show both fields in the same dialog. As you can see I've experimented with NSStackView (and others), but I have not found a solution to show both elements.
Hope will help you ...
You can use NSStackView and addSubview 2 item : unameField and passField.
But you must set frame for NSStackView and 2 item on NSStackView.
This is my code, you can refer:
let unameField = NSTextField(frame: NSRect(x: 0, y: 2, width: 200, height: 24))
let passField = NSSecureTextField(frame: NSRect(x: 0, y: 28, width: 200, height: 24))
let stackViewer = NSStackView(frame: NSRect(x: 0, y: 0, width: 200, height: 58))
stackViewer.addSubview(unameField)
stackViewer.addSubview(passField)
alert.accessoryView = stackViewer
And this is my result:
my result
My code: https://github.com/nhhthuanck/CustomizeNSAlertMacOS

Saving NSUserDefaults with UISwitch in Swift 2

I have posted all my code below to fully demonstrate what I am trying to accomplish. I have a UI Switch, that I would like to save a NSUserDefault on what game mode they would like. I am not sure why this is not working correctly, as it currently does not save- as the switch doesn't work.
import SpriteKit
class ModeScene: SKScene {
var modeSwitch = UISwitch()
var motionLabel = UILabel()
var touchLabel = UILabel()
override func didMoveToView(view: SKView) {
let MotionMode = NSUserDefaults.standardUserDefaults()
let TouchMode = NSUserDefaults.standardUserDefaults()
modeSwitch.center = CGPoint(x: view.center.x, y: view.frame.size.height / 1.3)
modeSwitch.addTarget(self, action: Selector("updateMode"), forControlEvents: UIControlEvents.ValueChanged)
if TouchMode {
modeSwitch.on = false
}
else{
modeSwitch.on = true
}
self.view?.addSubview(modeSwitch)
motionLabel = UILabel(frame: CGRect(x: 0, y: 0, width: 700, height: 200))
motionLabel.center = CGPoint(x: view.center.x * 3.4, y: view.frame.size.height / 1.3)
motionLabel.text = "Accelerometer"
motionLabel.textColor = UIColor.whiteColor()
motionLabel.font = UIFont(name:"ArialRoundedMTBold", size: 16)
self.view?.addSubview(motionLabel)
touchLabel = UILabel(frame: CGRect(x: 0, y: 0, width: 700, height: 200))
touchLabel.center = CGPoint(x: view.center.x * 2.25, y: view.frame.size.height / 1.3)
touchLabel.text = "Touch To Move"
touchLabel.textColor = UIColor.whiteColor()
touchLabel.font = UIFont(name:"ArialRoundedMTBold", size: 16)
self.view?.addSubview(touchLabel)
DoneBtn = UIButton(frame: CGRect(x: 0, y: 0, width: 400, height: 30))
DoneBtn.center = CGPoint(x: view.center.x * 1.7, y: view.frame.size.height / 14)
DoneBtn.setTitle("Done", forState: UIControlState.Normal)
DoneBtn.setTitleColor(UIColor.whiteColor(), forState: UIControlState.Normal)
DoneBtn.addTarget(self, action: Selector("done"), forControlEvents: UIControlEvents.TouchUpInside)
self.view?.addSubview(DoneBtn)
updateMode()
}
func updateMode(){
if modeSwitch.on {
// mode is accel
let MotionMode = NSUserDefaults.standardUserDefaults()
MotionMode.setValue(true, forKey: "MotionMode")
NSLog("Mode is accel")
}
else{
// mode is touch
let TouchMode = NSUserDefaults.standardUserDefaults()
TouchMode.setObject(true, forKey: "TouchMode")
NSLog("Mode is touch")
}
}
func done(){
self.scene?.view?.presentScene(StartScene(size: self.scene!.size), transition: SKTransition.fadeWithColor(UIColor.blackColor(), duration: 1))
headerLabel.removeFromSuperview()
DoneBtn.removeFromSuperview()
modeSwitch.removeFromSuperview()
touchLabel.removeFromSuperview()
motionLabel.removeFromSuperview()
}
}
Code Update:
import SpriteKit
class ModeScene: SKScene {
var modeSwitch = UISwitch()
var motionLabel = UILabel()
var touchLabel = UILabel()
var DoneBtn : UIButton!
override func didMoveToView(view: SKView) {
backgroundColor = SKColor.blackColor()
modeSwitch.center = CGPoint(x: view.center.x, y: view.frame.size.height / 1.3)
modeSwitch.addTarget(self, action: Selector("updateMode"), forControlEvents: UIControlEvents.ValueChanged)
self.view?.addSubview(modeSwitch)
motionLabel = UILabel(frame: CGRect(x: 0, y: 0, width: 700, height: 200))
motionLabel.center = CGPoint(x: view.center.x * 3.4, y: view.frame.size.height / 1.3)
motionLabel.text = "Accelerometer"
motionLabel.textColor = UIColor.whiteColor()
motionLabel.font = UIFont(name:"ArialRoundedMTBold", size: 16)
self.view?.addSubview(motionLabel)
touchLabel = UILabel(frame: CGRect(x: 0, y: 0, width: 700, height: 200))
touchLabel.center = CGPoint(x: view.center.x * 2.25, y: view.frame.size.height / 1.3)
touchLabel.text = "Touch To Move"
touchLabel.textColor = UIColor.whiteColor()
touchLabel.font = UIFont(name:"ArialRoundedMTBold", size: 16)
self.view?.addSubview(touchLabel)
DoneBtn = UIButton(frame: CGRect(x: 0, y: 0, width: 400, height: 30))
DoneBtn.center = CGPoint(x: view.center.x * 1.7, y: view.frame.size.height / 14)
DoneBtn.setTitle("Done", forState: UIControlState.Normal)
DoneBtn.setTitleColor(UIColor.whiteColor(), forState: UIControlState.Normal)
DoneBtn.addTarget(self, action: Selector("done"), forControlEvents: UIControlEvents.TouchUpInside)
self.view?.addSubview(DoneBtn)
let defaults = NSUserDefaults.standardUserDefaults()
if let motionMode = defaults.objectForKey("motionMode") as? Bool {
if motionMode {
// true
modeSwitch.on = true
} else {
// false
modeSwitch.on = false
}
}
updateMode()
}
func updateMode(){
let defaults = NSUserDefaults.standardUserDefaults()
if modeSwitch.on {
// mode is accel
defaults.setBool(true, forKey: "motionMode")
NSLog("Mode is accel")
}
else{
// mode is touch
defaults.setBool(true, forKey: "touchMode")
NSLog("Mode is touch")
}
}
func done(){
self.scene?.view?.presentScene(StartScene(size: self.scene!.size), transition: SKTransition.fadeWithColor(UIColor.blackColor(), duration: 1))
headerLabel.removeFromSuperview()
DoneBtn.removeFromSuperview()
modeSwitch.removeFromSuperview()
touchLabel.removeFromSuperview()
motionLabel.removeFromSuperview()
}
}
You forgot to actually load the values from NSUserDefaults.
You set them with setValue and setObject, but you never load them back.
Use the valueForKey or objectForKey methods to retrieve your values.
let defaults = NSUserDefaults.standardUserDefaults()
let MotionMode = defaults.objectForKey("MotionMode") as! Bool
if MotionMode {
// true
} else {
// false
}
Even better with safe unwrapping:
let defaults = NSUserDefaults.standardUserDefaults()
if let MotionMode = defaults.objectForKey("MotionMode") as? Bool {
if MotionMode {
// true
} else {
// false
}
}
A few notes: variables should begin with a lowercase letter, so "MotionMode" should be "motionMode", etc. You could use the specialized object getters like boolForKey instead of the generic objectForKey which forces you to typecast to Bool. Of course it goes with setBool on the other side. Last: the variable you call "MotionMode" actually holds NSUserDefaults.standardUserDefaults(), so I called it "defaults" instead in my example.
Your question has several parts, but it's better to make a single post for a single question. So I've focused about the failing NSUserDefaults part because it was the most important. Using my answer, if your code still doesn't work because of the UI parts, then you should post a new different question about it to keep things clear.

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)