I have a button that is already hooked up in the InterfaceBuilder to bring up a modal interface controller. In some cases I'd like to trigger this transition programmatically but I can't find a way to perform that same segue in Watchkit. I know you can do this in iphones/iOS but so far I can't find it in the Watchkit.
It can be done programatically. You need to set the Identifier in the storyBoard for the interface.
If you want it to present modally
presentControllerWithName("Identifier", context: nil)
If you want to push
pushControllerWithName("Identifier", context: nil)
You can set context if you like. You can retrieve it in the controller you push/present
override func awakeWithContext(context: AnyObject?) {
super.awakeWithContext(context)
}
the new calls:
presentController(withName: "Identifier", context: nil)
pushController(withName: "Identifier", context: nil)
Related
I'm loading an array with data from a Firebase database in my AppDelegate as I need the arrays loaded before the views are created and loaded. How can I pass this array to a view controller to populate a TableView?
Edit:
I don't think I worded my question correctly nor provided enough information. I have this Firebase data that I need to load into an array. This array needs to be loaded into the app before it is used by the view controller that uses the array. This is because the view controller uses a cocoa pod that splits the array into a number of categories to be displayed in different tableviews. The repo for the cocoa pod I'm using can be found here.
My question then is where is the best place to load this array? My first thought was the AppDelegate, but the array is empty and as such the table view doesn't load. I'm pretty new to iOS programming so I'm open to any and all suggestions.
In the AppDelegate class:
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {
//Fill your array, let's say it's called firebaseArray. I would recommend doing so in the view controller and not in the Appdelegate for better responsibility distribution in your code
let vc = YourViewController()
//And YourViewController must have a array property
vc.array = firebaseArray
window = UIWindow(frame: UIScreen.main.bounds)
//make your vc the root view view controller
window?.rootViewController = vc
window?.makeKeyAndVisible()
return true
}
In Swift 4 Xcode 9, if you load and present a Nib as follows, how do you dismiss it? This is an extract of my code in another ViewController from which the Nib gets loaded.
#objc func doPresent(_ sender: Any?) {
if let svc = Bundle.main.loadNibNamed("SecondViewController", owner: self, options: nil)?.first as? SecondViewController {
svc.data = "This is very important data!"
svc.delegate = self
self.present(svc, animated:true)
}
}
I tried:
self.view.subviews[0].removeFromSuperview()
and
self.view.removeFromSuperview()
as well as
self.dismiss(animated:true)
None of the three have the desired result. The first one removes a button from the Nib, the second just gives me a dark screen and the third dismisses the view in which the Nib is embedded to the original view in the navigation view.
I declared svc as a var at the class level and was able to use
self.v.removeFromSuperview()
That did the trick for me.
I have a playing field for a board game similar to chess with 60 fields. Instead of connecting them manually in storyboard I want to do it with a for...in.
My code is like this:
let feld = NSButton()
feld.frame = CGRect(x: 1300+30*h, y: 20, width: 22, height: 22)
feld.target = self
feld.action = #selector(ButtonTest)
self.view.addSubview(feld)
ButtonArray.append(feld)
And the function like this (I dont understand why I need #objc before the function but only this way it works.
#objc func ButtonTest() {
ButtonArray[2].isEnabled=false
}
The code work fine. But I want to pass an argument to the function so I can deactivate the button just being pressed and not only [2] like in this example.
(and I want to add other calculations to this function).
I searched around and it seems that one cannot pass arguments. If that is true how else can I solve my problem?
EDIT:
Great answer and explanation, thanks. I sadly asked the question imprecisely: I dont only want to deactivate the pressed button but also
do calculations like this:
var score = 5+/NumberOfButtonPressed/
So if the first button is pressed var score=6, if the 10th button is pressed score=15.
You need #objc because otherwise, the Swift compiler will not create a selector for the method. A selector is an Objective-C value that identifies a method, and it is necessary to call a method through Objective-C. (Cocoa is written in Objective-C.) You can get a method's selector using #selector, as you have already found.
On macOS, actions called through Cocoa take 0 or 1 additional parameters. The optional parameter is the "sender", which is the object that triggered the action (the button on which you clicked). Try this:
#objc
func ButtonTest(sender: NSButton?) {
sender?.isEnabled = false
}
You don't need to change the #selector() expression. The Cocoa runtime will call your method with that parameter just by virtue of it existing.
When I'm trying to switch from one view to another using Swift 4 on XCode 9, it gives me this error.
libc++abi.dylib: terminating with uncaught exception of type
NSException
I'm using a button to switch from Main to LandingView. Main is the default view(main.storyboard) while LandingView is another view controller within main.storyboard.
Here's the code I'm using to get this to work.
let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let newViewController = storyBoard.instantiateViewController(withIdentifier: "LandingView")
self.present(newViewController, animated: true, completion: nil)
[Above] All contained in a IBAction for the button I want to use.
Just to clarify, LandingView is a Storyboard ID that I'm using to make the switch from default view to LandingView. Please note this is all contained under Main.storyboard.
You haven't specified newViewController's type.
Try this:
let storyBoard: UIStoryBoard = UIStoryBoard(name: "Main", bundle: nil)
let newViewController = storyboard.instantiateViewController(withIdentifier: "LandingView") as! younewViewControllerType
self.present(newViewController, animated: true, completion: nil)
To determine what you have declared your LandingView ViewController Subclass, you can reference Identity Inspector in Interface builder as in the image below:
Under Custom Class, the Class property defines your custom subclass. When declaring your newViewController, you must declare it as that type.
I'm new to Swift and followed a simple tutorial to make a magic 8 ball Cocoa App that every time I click the ball it shows a different piece of advice. I am now trying to practice my UI automated tests by asserting (XCTAssert) that the "Piece of Advice" label is equal to one of the string values in my array.
My array looks like this and is in my ViewController.swift:
var adviceList = [
"Yes",
"No",
"Tom says 'do it!'",
"Maybe",
"Try again later",
"How can I know?",
"Totally",
"Never",
]
How can I make an assertion in my UITests.swift file that asserts that the string that is shown is equal to one of the string values in the array above?
It's possible that you're asking how to access application state from a UI test, or just in general UI testing.
I think it's a pretty interesting question so I'm going to answer because it's something that I don't know a lot about and hopefully will prompt other people to chime in and correct.
Background: A basic Magic 8 Ball project
I set up a basic project with a view controller that contains two views: a label and a button. Tapping the button updates the label text with a random message:
import UIKit
struct EightBall {
static let messages = ["Yes", "No", "It's not certain"]
var newMessage: String {
let randomIndex = Int(arc4random_uniform(UInt32(EightBall.messages.count)))
return EightBall.messages[randomIndex]
}
}
class ViewController: UIViewController {
let ball = EightBall()
#IBOutlet weak var messageLabel: UILabel!
#IBAction func shakeBall(_ sender: Any) {
messageLabel.text = ball.newMessage
}
}
A basic UI test
Here's a commented UI test showing how to automate tapping on the button, and grabbing the value of the label, and then checking that the value of the label is a valid message.
import XCTest
class MagicUITests: XCTestCase {
// This method is called before the invocation of each test method in the class.
override func setUp() {
super.setUp()
// In UI tests it is usually best to stop immediately when a failure occurs.
continueAfterFailure = true
// UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method.
XCUIApplication().launch()
}
func testValidMessage() {
// Grab reference to the application
let app = XCUIApplication()
// #1
// Grab reference to the label with the accesability identifier 'MessageLabel'
let messagelabelStaticText = app.staticTexts["MessageLabel"]
// Tap the button with the text 'Shake'
app.buttons["Shake"].tap()
// get the text of the label
let messageLabelText = messagelabelStaticText.label
// #2
// check if the text in the label matches one of the allowed messages
let isValidMessage = EightBall.messages.contains(messageLabelText)
// test will fail if the message is not valid
XCTAssert(isValidMessage)
}
}
At #1 The approach that I'm using to get the label is to access the labels accessibilityIdentifier property. For this project I entered this through storyboard, but if you're setting your views up in code you can directly set the accessibilityIdentifier property yourself.
The other thing that's confusing here is that to get access to elements in the view you're not navigating the view hierarchy, but a proxy of the hierarchy, which is why the syntax to get a label is the odd 'staticTexts' (The references at the bottom of the post explain this in more detail).
For #2 I'm inspecting the structure defined in my project. In a unit test you could access this my importing #testable import ProjectName but unfortunately this approach doesn't work for UI Test.
Instead, you'll have to make sure that any source file you want to access from the UI test is included as a target. You can do this in Xcode from this panel by checking the name of your UI test:
More UI testing references:
UI Testing Intro: http://www.mokacoding.com/blog/xcode-7-ui-testing/
UI Testing Cheat Sheet: http://masilotti.com/ui-testing-cheat-sheet/