Adding a ViewController to application without a storyboard - swift

How do you implement functions like viewDidLoad() in an empty project without a storyboard?
Sorry for the beginner question. I have been searching for hours now but it still doesn't work.
Thanks for any help!
edit:
I am using a .xib file, just no .storyboard. At the moment I'm setting everything up inside applicationDidFinishLaunching()
edit2:
I think I finally got it:
I created a new project without a storyboard, created a file ViewController.swift together with an ViewController.xib and added
window?.contentViewController = ViewController()
into the applicationDidFinishLaunching function inside the AppDelegate.
I cannot believe I didn't get this sooner.
Sorry BaseZen for your wasted time and thank you very much for trying to help me!

The question is (far) too broad. Please make it more specific, perhaps tailoring it to the part of it that is answered here, just for the sake of others in the future.
There are in-code equivalents to all Storyboard techniques. Your google technique is:
MacOS Swift how to doXyz programmatically
By the way, iOS is probably 10x more popular in terms of search results. So if the platform doesn't matter and you just want to learn how to do it in code, start with iOS.
This will get you started with layout: https://www.hackingwithswift.com/read/6/3/auto-layout-in-code-addconstraints
For example, initializing a non-IBOutlet:
var myButton = NSButton()
Setting its text:
myButton.title = "Hello"
Adding it to a View Controller's view:
view.addSubview(myButton)
Constraining it within the view:
// You have to learn some verbose weird stuff to do this in code
myButton.translatesAutoresizingMaskIntoConstraints = false
view.addConstraint(NSLayoutConstraint( ... ))
Connecting it to a non-IBAction handler, assuming you have a function called func handleAction(sender: AnyObject)
myButton.target = self
myButton.action = #selector(MyViewController.handleAction(_:))
Simplest possible example:
import Cocoa
class ViewController: NSViewController {
var b = NSButton()
override func viewDidLoad() {
super.viewDidLoad()
b.title = "Hi! I am a button"
b.target = self
b.frame = NSRect(x: 100, y: 100, width: 100, height: 25)
b.action = #selector(ViewController.handleButtonPress(_:))
view.addSubview(b)
}
func handleButtonPress(sender: NSButton) {
b.title = "Thank you for clicking."
b.sizeToFit()
}
}

Related

Programmatic beginRefreshing() on iOS11 has problems with largeTitles mode

We have found what seems to be a bug in UIKit but wanted to post here to see if anyone else has this problem or found a solution.
We're trying to use the new iOS11 large titles and hoisted search bar/refreshcontrol. We seemed to have found a problem where the root viewController of the navigation stack shows a minor display issue (problem A) but once another viewcontroller is pushed onto the navigation stack, the display goes nuts (problem B):
Things to note:
The problem is worse on the 2nd VC in the stack rather than the 1st
The refreshControl is not the green color the code sets it to the 1st time you see it on each sceen
The refreshControl slides down as you pull to refresh, it shouldn't do this
This odd behavior seems to only be a problem when we programmatically do a "pull to refresh" in viewDidLoad so that the user can see that the data is loading when they enter the screen. If we remove the lines that invoke refreshControl?.beginRefreshing() the display is clean. I've recreated this problem in a sample vanilla app. This is the entirety of the viewcontroller that shows the problem:
import UIKit
class ViewController: UITableViewController {
var tableHeaderSearchController: UISearchController!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.navigationController?.navigationBar.prefersLargeTitles = true
self.navigationController?.navigationItem.largeTitleDisplayMode = .always
tableHeaderSearchController = UISearchController(searchResultsController: UITableViewController())
navigationItem.searchController = tableHeaderSearchController
refreshControl?.tintColor = UIColor.green
refreshControl?.backgroundColor = UIColor.clear
refreshControl?.attributedTitle = NSAttributedString(string: "Loading Stuff...", attributes: [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 17)])
refreshControl?.addTarget(self, action: #selector(refreshPulled), for: .valueChanged)
// Commenting out these 2 lines makes it work fine but you can't see the initial refresh spinner
refreshControl?.beginRefreshing()
refreshPulled()
}
#objc func refreshPulled() {
DispatchQueue.main.asyncAfter(deadline: .now() + 3) { [refreshControl] in
refreshControl?.endRefreshing()
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Here's the storyboard. It's just a vanilla tableviewcontroller wrapped in a navigationController. 3 static cells, the 2nd one traverses to another instance of the same controller type.
Any ideas would be greatly appreciated. We'd really like to adopt the new look but this stuff is making it very hard to do so.
First, it is absolutely crucial that the table view extend up underneath the navigation bar and that is iOS 11 offset behavior be correct:
self.edgesForExtendedLayout = .all
self.tableView.contentInsetAdjustmentBehavior = .always
Second, scrolling to show the refresh control when you refresh manually is up to you, and calculating the amount is not at all simple:
self.refreshControl!.sizeToFit()
let top = self.tableView.adjustedContentInset.top
let y = self.refreshControl!.frame.maxY + top
self.tableView.setContentOffset(CGPoint(0, -y), animated:true)
self.refreshControl!.beginRefreshing()
The bar still stays too big during the refresh, but I don't see what can be done about that. Basically Apple has implemented large titles and shown the refresh control in the nav bar without thinking through the effects or dealing with the resulting bugs.

view.endEditing causing app to freeze on some textFields

Problem solved. See end of post.
Sorry if this is a bit long but I'm hoping I've included as much info to get this solved.
Brief overview of problem: Enter value in a textField using my custom keypad. Tap done button(should trigger view.endEditing) and some textFields will cause the app to freeze, most the time Xcode won't even throw an error but instead just restart the app, but i did catch one once(pic below). It works as expected on some textFields.
So I have a view controller with a bunch of textFields for the user to fill out which then performs calculations.
I have made a custom Keypad which essentially is the decimal pad with a "Done" button. I did this by making an keyboard.xib file and a keyboard.swift file.
Heres a snapshot of the error, I've included a whole bunch of my code below incase I'm using a method that isn't the best.
This is how the keyboard.swift file looks:
import UIKit
// The view controller will adopt this protocol (delegate)
// and thus must contain the keyWasTapped method
protocol KeyboardDelegate: class {
func keyWasTapped(character: String)
func keyDone()
func backspace()
}
class keyboard: UIView {
// This variable will be set as the view controller so that
// the keyboard can send messages to the view controller.
weak var delegate: KeyboardDelegate?
// MARK:- keyboard initialization
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
initializeSubviews()
}
override init(frame: CGRect) {
super.init(frame: frame)
initializeSubviews()
}
func initializeSubviews() {
let xibFileName = "Keyboard" // xib extention not included
let view = NSBundle.mainBundle().loadNibNamed(xibFileName, owner: self, options: nil)[0] as! UIView
self.addSubview(view)
view.frame = self.bounds
}
// MARK:- Button actions from .xib file
#IBAction func keyTapped(sender: UIButton) {
// When a button is tapped, send that information to the
// delegate (ie, the view controller)
self.delegate?.keyWasTapped(sender.titleLabel!.text!) // could alternatively send a tag value
}
#IBAction func backspace(sender: UIButton) {
self.delegate?.backspace()
}
#IBAction func Done(sender: UIButton) {
self.delegate?.keyDone()
}
}
In the viewController I'm pretty sure I've included all the necessary things to access the keyboard seeing as it works for some textFields. Such as:
class myViewController: UITableViewController,UITextFieldDelegate, KeyboardDelegate
Then in viewDidLoad set each textField delegate:
self.textField1.delegate = self
self.textField2.delegate = self
self.textField3.delegate = self
// initialize custom keyboard
let keyboardView = keyboard(frame: CGRect(x: 0, y: 0, width: 0, height: numpad.height))
keyboardView.delegate = self // the view controller will be notified by the keyboard whenever a key is tapped
// replace system keyboard with custom keyboard
textField1.inputView = keyboardView
textField2.inputView = keyboardView
textField3.inputView = keyboardView
Then this function (which seems to me to be the problem):
func keyDone() {
view.endEditing(true)
//activeTextField.resignFirstResponder()
print("please dont freeze")
}
I have checked all the connections, they seem to be fine.
Let me know if I can add any more info to help work it out.
Many Thanks.
Solved!!!
I suppose ill just put it down to beating my head over it rather than taking a break from the screen! Still I'm confused why it wasn't given a more specific error.
The problem was that in some cases one of the functions was dividing by zero (this is undefined... not possible) but a good thing to take from this(thank you Olivier) is the Instruments Tools to help find where abouts the code was losing its mind. This tutorial helped me understand how to use instruments! So once I could see where it was going crazy I set up a bunch of print statements to watch the values as they went into the 'problem' calculation, where I found the denominator to be zero. Bit of rearranging the code around to avoid this and problem solved!
This error message is basically saying that there is a memory issue, try running the code with instruments (Allocations in particular) this might reveal is there is something amiss with your keyboard
Edit 2: for anyone finding this error message in future (actual solution in this case)
Double check any code code running after keyDone() to see if there are any infinite loops or situations that would cause the compiler to assume an infinite amount of memory is required. In this case a line of code was dividing by zero, causing a fatal memory error (unable to allocate the N/A value it generated)

Simple NSPageController example throws an unknown subview warning and stops working

I'm trying to get a very basic NSPageController to work (in book mode, not history mode). It will successfully transition once, and then stop working.
I suspect I'm creating the NSImageViews I'm loading into it wrong, but I can't figure out how.
The storyboard has a the SamplePageController which holds in initial hard-coded NSImageView.
I suspect I'm missing something really obvious here, since all of the tutorial's I've found for NSPageController are in Objective C not swift, and tend to focus on the history view mode.
The code is:
import Cocoa
class SamplePageController: NSPageController, NSPageControllerDelegate {
private var images = [NSImage]()
#IBOutlet weak var Image: NSImageView!
//Gets an object from arranged objects
func pageController(pageController: NSPageController, identifierForObject object: AnyObject) -> String {
let image = object as! NSImage
let image_name = image.name()!
let temp = arrangedObjects.indexOf({$0.name == image_name})
return "\(temp!)"
}
func pageController(pageController: NSPageController, viewControllerForIdentifier identifier: String) -> NSViewController {
let controller = NSViewController()
let imageView = NSImageView(frame: Image.frame)
let intid = Int(identifier)
let intid_u = intid!
imageView.image = images[intid_u]
imageView.sizeToFit()
controller.view = imageView
return controller
// Does this eventually lose the frame since we're returning the new view and then not storing it and the original ImageView is long gone by then?
// Alternatively, are we not sizing the imageView appropriately?
}
override func viewDidLoad() {
super.viewDidLoad()
images.append(NSImage(named:"text")!)
images.append(NSImage(named:"text-2")!)
arrangedObjects = images
delegate = self
}
}
In this case your pageController.view is set to your window.contentView and that triggers the warning. What you need to do is add a subview in the window.contentView and have your pageController.view point to that instead.
The reason for the warning is that since NSPageController creates snapshots (views) of your content history, it will add them at the same level as your pageController.view to transition between them: that means it will try to add them to pageController.view.superview.
And if your pageController.view is set to window.contentView, you are adding subviews to the window.contentView.superview, which is not supported:
New since WWDC seed: NSWindow has never supported clients adding subviews to anything other than the contentView.
Some applications would add subviews to the contentView.superview (also known as the border view of the window). NSWindow will now log when it detects this scenario: "NSWindow warning: adding an unknown subview:".
Applications doing this will need to fix this problem, as it prevents new features on 10.10 from working properly. See titlebarAccessoryViewControllers for official API.

swift dragUP navigation

I'm totally new to coding and was already very happy to get swipe down functionality into my app.
Now I would like to have a swipe up, so if I drag it down I have a UIView that can have some sort of navigation/option screen (all that code is working perfectly) but I want it the other way around (so you can see that it's down there and then you'd swipe it up). I followed a tutorial online that showed me how to get it like this and after I implemented the first code it's already up there and I tried a couple of things but I don't seem to get it.. here's the code:
class ViewController: UIViewController {
var myview:myView!
override func viewDidLoad() {
super.viewDidLoad()
myview = NSBundle.mainBundle().loadNibNamed("customView", owner: self, options: nil).last as signUpView
myview.frame = CGRectMake(0, -self.view.frame.size.height + 66, self.view.frame.size.width, self.view.frame.size.height)
self.view.addSubview(signup)
myview.setup()

swift iMessage style keyboard input

i am trying to implement an iMessage style keyboard input with a textview that sits at the bottom of the screen then slides up with the keyboard when you touch the textView, then is docked to the top of the keyboard.
I found MessageComposerView which is exactly what I want. Unfortunately I cannot get it working ( I am using swift).
below is my code:
import UIKit
class CommentsViewController: UIViewController, MessageComposerViewDelegate {
var messageComposerView: MessageComposerView!
override func viewDidLoad() {
super.viewDidLoad()
let defaultWidth = view.frame.size.width
let defaultHeight = CGFloat(54.0)
let subviewFrame = CGRect(x: 0, y: view.frame.height - defaultHeight, width: defaultWidth, height: defaultHeight)
messageComposerView = MessageComposerView(frame: subviewFrame) as MessageComposerView
view.addSubview(messageComposerView)
}
func messageComposerSendMessageClickedWithMessage(message: String!) {
}
}
however it does not show up. Ive printed the view and its frame is correct, its just that there is nothing there for some reason. Everything looks like it should be working. Does anyone see anything wrong with my current implementation?
Try PHFComposeBarView Library (https://github.com/fphilipe/PHFComposeBarView), it's a exact copy of the iMessage composer bar that can be used in C & Swift from storyboard or code
Code Example : https://github.com/liveminds/SwiftPHFComposeBarTest
Storyboard Example : https://github.com/liveminds/SwiftPHFComposeBarTest/tree/storyboard-managed
To add the bar to your view:
drag a new UIView on your UIViewcontroller, assign "PHFComposeBarView" class to UIView
Add an outlet of the UIView to your Viewcontroller's class
add "PHFComposeBarViewDelegate" to your Viewcontroller's class
assign UIView Delegate in viewdidload() : self.composerBarOutlet.delegate = self
Set the composer bar to appear as inputAccessoryView above the keyboard:
override var inputAccessoryView: UIView {
composerBar.removeFromSuperview()
return composerBar
}
Style your Bar(example):
composerBar.utilityButtonImage = UIImage(named: "fullStar")!
composerBar.buttonTitle = "Submit"
composerBar.maxCharCount = 200
composerBar.maxLinesCount = 5
composerBar.alpha = 1.0
composerBar.buttonTintColor = AppConfig.BLUECOLOR
composerBar.placeholder = "What do you think about this product?"
Try following this example by Andrew Bancroft Send Text Message In-App – Using MFMessageComposeViewController with Swift. His example provides a nice walkthrough using Swift and includes sample code on GitHub.
Just going from the code snippet included in your post, it looks like you need to import Foundation, import MessageUI, conform to the MFMessageComposeViewControllerDelegate protocol, and implement the messageComposeViewController protocol method. All of this is covered in Andrew's blog post. This should give you what you're looking for.
Have you find the answer yet?
I use the same Framework however I believe you need to set up the delegate to self. IN my case it fails though I dont know why but it is written down in the readme github.