I am trying to develop a basic app in Xcode 6.1 and Swift language.
I cant seem to find any IOS 8, Xcode6 and Swift resources yet that help with this. Everything is still xcode 5 and IOS 7.
I have the whole story board done with images buttons and segues. just need to script it.
There are 6 segues all with segue "show".
I am just trying to capture one text field per view controller. upon button press for the segue to the next view controller.
the prepareForSegue thing confuses me.
how do I capture the data from a text field save it to a variable on button press and send that variable info to the next view controller?
That text field I want saved to a variable or let that I can have the final view controller access all the data input from each view controller and then do some basic math.
Any body can help with this?
Assuming that you have two view Controllers here is how you will pass value to the second view controller.
class ViewControllerA: UIViewController {
#IBOutlet var textField: UITextField = nil
...
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
if (segue.identifier == "segueTest") {
var svc = segue!.destinationViewController as ViewControllerB;
svc.passedValue = textField.text
}
}
}
class ViewControllerB: UIViewController {
var passedValue: String?
...
override func viewDidLoad() {
...
if let myPassedData = passedValue {
println(myPassedData)
}
or use
let myPassedData = passedValue ?? ""
println(myPassedData)
}
}
}
Related
I have a textfield with a hidden keyboard (since I'm using it with bluetooth). However, in iOS9 the shortcut bar keeps appearing.
Is there a way to hide it too?
Thank you so much!
You can pass your textfield name in place of userNameTextField for which you want to remove shortcut bar.
UITextInputAssistantItem* item = [userNameTextField inputAssistantItem];
item.leadingBarButtonGroups = #[];
item.trailingBarButtonGroups = #[];
In Swift 2.0
if #available(iOS 9.0, *) {
let item : UITextInputAssistantItem = yourTextView.inputAssistantItem
item.leadingBarButtonGroups = []
item.trailingBarButtonGroups = []
} else {
// Fallback on earlier versions
}
I had the same issue. And so starts a search of SO. So the above helped me out, but the whole, "if iOS9 thing" might be best framed like this:
if ([self respondsToSelector:#selector(inputAssistantItem)]) {
// iOS9.
UITextInputAssistantItem* item = [self inputAssistantItem];
item.leadingBarButtonGroups = #[];
item.trailingBarButtonGroups = #[];
}
Happily, I'd created a sub-class of a UITextField, (CHTextField) and was in use everywhere. So it was a very easy fix to whack this in the over-ridden "init" method.
Hope it helps.
Alternatively, just create an extension for UITextField in Swift 2.0 like this.
extension UITextField
{
public func hideAssistantBar()
{
if #available(iOS 9.0, *) {
let assistant = self.inputAssistantItem;
assistant.leadingBarButtonGroups = [];
assistant.trailingBarButtonGroups = [];
}
}
}
Then you can just call hideAssistantBar() on any text field you like.
#IBOutlet weak var myTextField: UITextField?;
override public func viewDidLoad() {
super.viewDidLoad();
myTextField?.hideAssistantbar();
}
In Swift 3.0 and 4.0
self.textField.inputAssistantItem.leadingBarButtonGroups.removeAll()
self.textField.inputAssistantItem.trailingBarButtonGroups.removeAll()
An easy way to do this for all text fields in your app is to create a category on UITextInputAssistantItem and override the getters for leadingBarButtonGroups and trailingBarButtonGroups like this:
#implementation UITextInputAssistantItem (RemoveBars)
- (NSArray<UIBarButtonItemGroup *> *)leadingBarButtonGroups
{
return #[];
}
- (NSArray<UIBarButtonItemGroup *> *)trailingBarButtonGroups
{
return #[];
}
#end
This worked for me on iOS 9.x and 8.x, no need for any conditional code.
Be careful with this though, this overrides those properties for EVERYTHING that uses UITextInputAssistantItem
Just to expand on the other answers here. I cobbled together some Swift 2.0 code that will loop through all subviews of a given view and disable the UITextInputAssistantItems for all UITextFields and UISearchBars.
func hideTheAssistantBar(view:UIView) {
//Check this view
for case let textField as UITextField in view.subviews {
let item : UITextInputAssistantItem = textField.inputAssistantItem
item.leadingBarButtonGroups = []
item.trailingBarButtonGroups = []
}
for case let searchBar as UISearchBar in view.subviews {
let item : UITextInputAssistantItem = searchBar.inputAssistantItem
item.leadingBarButtonGroups = []
item.trailingBarButtonGroups = []
}
//Now find this views subviews
let subviews = view.subviews
for subview : AnyObject in subviews {
if subview.isKindOfClass(UIView) {
hideTheAssistantBar(subview as! UIView)
}
}
}
You can then call this function passing in whatever view you would like to start at. I call this inside of my ViewDidLoad() method and pass in self.view like hideTheAssistantBar(self.view).
I actually went a step further for my needs and added this function to a helper class I use for common code. Therefore inside of my viewDidLoad() function I actually just call helper.hideTheAssistantBar(self.view) and then I don't have to put that function in every file.
Hope this helps someone coming along looking for an easy way to remove the assistant bar from all UITextFields and UISearchBars in one fail swoop.
Thanks to #Arkader for the swift code to recursively find all subviews. Swift List Subviews
Just to build on what Pranavan posted because setting the bar button groups to an empty array doesn't seem to work in iOS 12 or 13 using Xcode 11.
let inputAssistantItem = textFieldForTypingText.inputAssistantItem
inputAssistantItem.leadingBarButtonGroups.removeAll()
inputAssistantItem.trailingBarButtonGroups.removeAll()
I placed the above code in the viewDidLoad() function.
You can also give the option to the user:
inputAssistantItem.allowsHidingShortcuts = true
In the case letting the user hide it, if the text field becomes first responder again, they'll have to hide it again.
I am attempting to present a sheet configuration view (AddSoundEffect) for my main window/view controller (I'm using storyboards), and when the configuration view controller is dismissed, take the values entered in the AddSoundEffect view and pass that back to the main view. My current code in the main view controller:
presentViewControllerAsSheet(self.storyboard!.instantiateControllerWithIdentifier("AddSoundEffect") as! AddSoundViewController
And in the AddSoundViewController.swift file, the code to dismiss it is:
self.dismissViewController(self)
To pass the data, I have a class-independent tuple that I save data to. How do I add a completion handler to presentViewControllerAsSheet, and (optionally) is there a better way to pass the data between view controllers?
Setup: Xcode version 6.4, OS X 10.10.4
Delegation pattern is the easiest way for you.
// Replace this with your tuple or whatever data represents your sound effect
struct SoundEffect {}
protocol AddSoundViewControllerDelegate: class {
func soundViewController(controller: AddSoundViewController, didAddSoundEffect: SoundEffect)
}
//
// Let's say this controller is a modal view controller for adding new sound effects
//
class AddSoundViewController: UIViewController {
weak var delegate: AddSoundViewControllerDelegate?
func done(sender: AnyObject) {
// Dummy sound effect info, replace it with your own data
let soundEffect = SoundEffect()
//
// Call it whenever you would like to inform presenting view controller
// about added sound effect (in case of Done, Add, ... button tapped, do not call it
// when user taps on Cancel to just dismiss AddSoundViewController)
//
self.delegate?.soundViewController(self, didAddSoundEffect: soundEffect)
// Dismiss self
self.dismissViewControllerAnimated(true, completion: {})
}
}
//
// Let's say this controller is main view controller, which contains list of all sound effects,
// with button to add new sound effect via AddSoundViewController
//
class SoundEffectsViewController: UIViewController, AddSoundViewControllerDelegate {
func presentAddSoundEffectController(sender: AnyObject) {
if let addSoundController = self.storyboard?.instantiateViewControllerWithIdentifier("AddSoundEffect") as? AddSoundViewController {
addSoundController.delegate = self
self.presentViewController(addSoundController, animated: true, completion: {})
}
}
func soundViewController(controller: AddSoundViewController, didAddSoundEffect: SoundEffect) {
// This method is called only when new sound effect is added
}
}
Another way is to use closures:
// Replace this with your tuple or whatever data represents your sound effect
struct SoundEffect {}
//
// Let's say this controller is a modal view controller for adding new sound effects
//
class AddSoundViewController: UIViewController {
var completionHandler: ((SoundEffect) -> ())?
func done(sender: AnyObject) {
// Dummy sound effect info, replace it with your own data
let soundEffect = SoundEffect()
//
// Call it whenever you would like to inform presenting view controller
// about added sound effect (in case of Done, Add, ... button tapped, do not call it
// when user taps on Cancel to just dismiss AddSoundViewController)
//
self.completionHandler?(soundEffect)
// Dismiss self
self.dismissViewControllerAnimated(true, completion: {})
}
}
//
// Let's say this controller is main view controller, which contains list of all sound effects,
// with button to add new sound effect via AddSoundViewController
//
class SoundEffectsViewController: UIViewController {
func presentAddSoundEffectController(sender: AnyObject) {
if let addSoundController = self.storyboard?.instantiateViewControllerWithIdentifier("AddSoundEffect") as? AddSoundViewController {
addSoundController.completionHandler = { [weak self] (soundEffect) -> () in
// Called when new sound effect is added
}
self.presentViewController(addSoundController, animated: true, completion: {})
}
}
}
Or many other ways like sending notification, ... Whatever suits your needs. But delegation pattern or closures is the best way to go in this specific case.
I missed that your question is about NSViewController. This example is for iOS, but same pattern can be used on OS X without any issues.
The easiest way to detect sheet opening or closing is to use the Sheet Notifications:
class ViewController: NSViewController, NSWindowDelegate {
override func viewDidLoad(){
NSApplication.sharedApplication().windows.first?.delegate = self
}
func windowDidEndSheet(notification: NSNotification) {
}
func windowWillBeginSheet(notification: NSNotification) {
}
}
I've created a simple NSViewController and want to add a split view with just one child view. The split view should be controlled by a NSSplitViewController, because I'd like to use the NSSplitItem's facilities for collapsing/expanding split items. After adding a child view controller, the split item is created, but no child view is added to the view tree.
override func viewDidLoad() {
super.viewDidLoad()
let splitViewController = NSSplitViewController()
view.addSubview(splitViewController.splitView)
let myController = MyController(nibName: "MyController", bundle: nil)
splitViewController.addChildViewController(myController)
printTree(view)
}
func printTree(view: AnyObject, _ n: Int = 1) {
if let view = view as? NSView {
NSLog("\(n): \(view)")
for child in view.subviews {
printTree(child, n + 1)
}
}
}
Output:
1: <NSView: 0x618000120140>
2: <NSSplitView: 0x6180001205a0>
Why does the split view have no child view?
To compare, here's the version without split view:
override func viewDidLoad() {
super.viewDidLoad()
let myController = MyController(nibName: "MyController", bundle: nil)
view.addSubview(myController.view)
printTree(view)
}
Output:
1: <NSView: 0x6100001203c0>
2: <NSView: 0x6000001208c0> <-- here's my child view
3: <NSButton: 0x600000140580>
And adding the child view directly as a subview to the split view doesn't work either:
A SplitView managed by a SplitViewController cannot have its subviews modified
So, my question is, why is the child view not added to the view tree inside the split view?
"You're doing it wrong"
You're using base class methods when NSSplitViewController has a very particular API.
See: https://developer.apple.com/library/prerelease/mac/samplecode/Exhibition/Listings/Exhibition_GalleryWindowController_swift.html for an example.
You want the addSplitViewItem: method.
I figured it out. My mistake was that I added the splitView instead of the view:
// this won't work:
self.view.addSubview(splitViewController.splitView)
// this will work:
self.view.addSubview(splitViewController.view)
BTW: using splitViewController.addChildViewController(myController) as I did before is just a shorter way of saying the following:
let item = NSSplitViewItem(viewController: myController)
splitViewController.addSplitViewItem(item)
which didn't work for me because of my mistake described above.
I am incredibly new to this, so please keep that in mind!
I've been at this all night, watched countless videos/haunted
countless forums...I can't find one single answer!
I am trying to make a basic popup menu in Swift/OSX What I need to figure out is:
How can I add more than the 'three items' to this menu
Whatever is selected in the popup, for that info to send an integer
value to another number.
I very much would appreciate your help, Thanks.
A NSPopupButton is a container for a bunch of NSMenuItem objects so to add an item you can use
func addItemWithTitle(_ title: String!)
The NSMenuItem gets constructed for you by the call.
and as you may wish to start from scratch you can use
func removeAllItems()
To clean existing items from the button.
There are also other methods around moving and removing menu items from the button.
A NSPopupButton is-a NSControl so you can use var action: Selector to set the action sent when an item is selected and var target: AnyObject! to control which object receives the message. Or just wire it up in Interface Builder.
protocol FooViewDelegate{
func itemWithIndexWasSelected(value:Int)
}
class FooViewController: NSViewController {
#IBOutlet weak var myPopupButton: NSPopUpButton!
var delegate: FooViewDelegate?
let allTheThings = ["Mother", "Custard", "Axe", "Cactus"]
override func viewDidLoad() {
super.viewDidLoad()
buildMyButton()
}
func buildMyButton() {
myPopupButton.removeAllItems()
myPopupButton.addItemsWithTitles(allTheThings)
myPopupButton.target = self
myPopupButton.action = "myPopUpButtonWasSelected:"
}
#IBAction func myPopUpButtonWasSelected(sender:AnyObject) {
if let menuItem = sender as? NSMenuItem, mindex = find(allTheThings, menuItem.title) {
self.delegate?.itemWithIndexWasSelected(mindex)
}
}
}
All the button construction can be done in Interface Builder rather than code too. Remember that you can duplicate items with CMD-D or you can drag new NSMenuItem objects into the button.
I have been looking for an answer for this, but have only found answers for segues.
I have viewController1 with a button that segues to viewController2. There is no code for this, I set it up through Interface builder. On viewController2 I have a button that dismisses itself with
self.dismissViewControllerAnimated(true, completion, nil)
I want to pass a string from viewController2 back to viewController1 when the view is dismissed. How do I go about doing this? Also, I am using swift.
Thanks in advance!
There are two common patterns, both of which eliminate the need for viewController2 to know explicitly about viewController1 (which is great for maintainability):
Create a delegate protocol for your for viewController2 and set viewController1 as the delegate. Whenever you want to send data back to viewController1, have viewController2 send the "delegate" the data
Setup a closure as a property that allows passing the data. viewController1 would implement that closure on viewController2 when displaying viewController2. Whenever viewController2 has data to pass back, it would call the closure. I feel that this method is more "swift" like.
Here is some example code for #2:
class ViewController2 : UIViewController {
var onDataAvailable : ((data: String) -> ())?
func sendData(data: String) {
// Whenever you want to send data back to viewController1, check
// if the closure is implemented and then call it if it is
self.onDataAvailable?(data: data)
}
}
class ViewController1 : UIViewController {
func doSomethingWithData(data: String) {
// Do something with data
}
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
// When preparing for the segue, have viewController1 provide a closure for
// onDataAvailable
if let viewController = segue.destinationViewController as? ViewController2 {
viewController.onDataAvailable = {[weak self]
(data) in
if let weakSelf = self {
weakSelf.doSomethingWithData(data)
}
}
}
}
}
I used the code from the first answer in a transition between controllers WITHOUT prepareForSegue and worked for me as well.
Here's the sample code.
The First View Controller:
#IBAction func dpAgendaClick(sender:UIBarButtonItem) {
///instantiating view controller with identifier
if let datePickerViewController = storyboard?.instantiateViewControllerWithIdentifier("DatePickerViewController")
as? DatePickerViewController {
///bring instantiated view controller to front
self.presentViewController(datePickerViewController, animated: true, completion: nil)
///wrapping the data returned
datePickerViewController.onDataFiltroAvailable = {[weak self]
(dataFiltro) in
if let weakSelf = self {
///use dataFiltro here
}
}
The second View Controller:
var onDataFiltroAvailable: ((dataFiltro: String) -> ())?
///private var
var dataFiltro: String = ""
///the returning data is obtained on the datePickerChanged event
#IBAction func datePickerChanged(sender: UIDatePicker) {
let dateFormatter = NSDateFormatter()
dateFormatter.dateStyle = NSDateFormatterStyle.ShortStyle
dateFormatter.dateFormat = "yyyy-MM-dd"
dataFiltro = dateFormatter.stringFromDate(datePicker.date)
}
///dismiss the controller on button click
#IBAction func dpOkClick(sender: UIButton) {
///"returning" the data
self.onDataFiltroAvailable?(dataFiltro: dataFiltro)
dismissViewControllerAnimated(true, completion: nil)
}
(Swift 2.1, Xcode 7, iOS9)
If you don't want it to be tightly coupled only between 2 ViewControllers,
You can also use the Notification Design Pattern (Post & Observe), which is mainly used to pass on the same object/information from one VC to multiple View Controllers.
For your scenario :
In VC2.swift :
#IBAction func BackBtn(sender: UIButton) {
NSNotificationCenter.defaultCenter().postNotificationName("ThisIsTheMessage", object: nil, userInfo:["ObjectBeingSent":yourObject])
}
And in VC1.swift :
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("yourFunction:"), name: "ThisIsTheMessage", object: nil)
}
func yourFunction(theNotification : NSNotification) {
if let extractInfo = theNotification.userInfo {
//code to use the object sent from VC2, by extracting the object details
}
}
Common Practise is:
Pass data forward -> Use PrepareForSegue
Pass data backward to the previous View Controller-> Protocol and Delegation
Pass data across multiple View Controllers -> Notifications : Post and Observe(observe in all the View controllers where you are using the object details)