iOS use Swift files as first view - ios

In Xcode 6, is it possible to use a .swift file as the 1st view the user will see when they open my app?
The file below is for a table view as I prefer to use swift instead of IB (too many views makes IB look messy).
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var tableView: UITableView!
let items = ["Hello 1", "Hello 2", "Hello 3"]
override func viewDidLoad() {
super.viewDidLoad()
self.view.frame = CGRect(x: 0, y: 0, width: 320, height: 480)
self.tableView = UITableView(frame:self.view.frame)
self.tableView!.dataSource = self
self.tableView!.registerClass(UITableViewCell.self, forCellReuseIdentifier: "cell")
self.view.addSubview(self.tableView)
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return self.items.count;
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as UITableViewCell
cell.textLabel.text = "\(self.items[indexPath.row])"
return cell
}
}
var ctrl = ViewController()

According to Apple's View Controller Programming Guide, "When working with view controllers directly, you must write code that instantiates the view controller, configures it, and displays it." However it also states "You gain none of the benefits of storyboards, meaning you have to implement additional code to configure and display the new view controller." That being said if you still desire to do this, this is how you do it (in your app delegate):
var window: UIWindow?
var viewController: ViewController?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
window = UIWindow(frame: UIScreen.mainScreen().bounds)
window?.backgroundColor = UIColor.whiteColor()
viewController = ViewController()
// Any additional setup
window?.rootViewController = viewController
window?.makeKeyAndVisible()
return true
}

Related

iOS: RefreshControl + large title doesn't work well in UITabBarController

I have UIViewController with NavigationBar that is a part of UITabController.
Inside UIViewController I have only UITableView.
NavigationBar is transparent and blurred.
TableView top constraint is to superview, so when I scroll content it goes behind navigation bar.
I have large titles enabled:
Problem:
loadData data triggered immediately when I start to scroll down.
Right after I scroll few pixels down.
All works fine if I remove largeTitles,
but with large titles it feels like refreshControl already at position when it ready to trigger .valueChanged
Also it works fine if to remove tab bar and load navigationController directly as root. But I need tabbar.
Full code:
#main
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let window = UIWindow(frame: UIScreen.main.bounds)
window.makeKeyAndVisible()
self.window = window
window.rootViewController = TabBarController()
return true
}
}
class TabBarController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
let navigationController = UINavigationController(rootViewController: ViewController())
navigationController.navigationBar.prefersLargeTitles = true
viewControllers = [navigationController]
}
}
class ViewController: UIViewController {
private let tableView = UITableView()
private let refreshControl = UIRefreshControl()
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.title = "Test"
view.addSubview(tableView)
tableView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
tableView.delegate = self
tableView.dataSource = self
tableView.refreshControl = refreshControl
refreshControl.addTarget(self, action: #selector(loadData), for: .valueChanged)
}
#objc func loadData() {
print("loadData triggered")
DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) {
self.refreshControl.endRefreshing()
}
}
}
extension ViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell: UITableViewCell
if let defaultCell = tableView.dequeueReusableCell(withIdentifier: "defaultCell") {
cell = defaultCell
} else {
cell = UITableViewCell(style: .value1, reuseIdentifier: "defaultCell")
}
cell.textLabel?.text = "Test"
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
10
}
}
BUT
All works fine if it's from StoryBoard:

How to fix iOS10 safe area and topLayoutGuide issue?

I have a question about safe area.
And I write a code and build in two simulators of iOS version 10.2 and 11.4.
It shows different part of red rectangle area like following image.
What's wrong with me?
Thanks.
code here:
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
window = UIWindow(frame: UIScreen.main.bounds)
window?.rootViewController = UINavigationController(rootViewController: ViewController())
window?.makeKeyAndVisible()
return true
}
}
class ViewController: UIViewController {
let tableView = UITableView()
override func viewDidLoad() {
super.viewDidLoad()
self.title = "TITLE HERE"
tableView.dataSource = self
tableView.delegate = self
self.view.addSubview(tableView)
tableView.snp.makeConstraints { (make) in
make.left.equalTo(10)
make.top.equalTo(self.topLayoutGuide.snp.bottom)
make.right.bottom.equalTo(-10)
}
}
}
extension ViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 20
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
cell.textLabel?.text = "\(indexPath) TEST"
return cell
}
}

SearchResultsController obscures screen when used with UINavigationItem

I'm using new integrated UISearchBar available in iOS 11, the problem is as soon as I tap on the searchbar and start typing text, the SearchResultsController obscures the whole screen, including the searchbar. It is impossible to dismiss the results controller or cancel search afterwards.
To demonstrate the issue, I've configured a minimal reproducible example:
import UIKit
let reuseid = "reuseIdentifier"
class TableViewController: UITableViewController, UISearchResultsUpdating {
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(UITableViewCell.self, forCellReuseIdentifier: reuseid)
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: reuseid, for: indexPath)
return cell
}
func updateSearchResults(for searchController: UISearchController) {
print(searchController.searchBar.text)
}
}
class ViewController: UIViewController {
var searchController: UISearchController!
override func viewDidLoad() {
super.viewDidLoad()
searchController = UISearchController(searchResultsController: TableViewController())
navigationItem.searchController = searchController
}
}
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
window = UIWindow(frame: UIScreen.main.bounds)
window?.backgroundColor = UIColor.white
window?.makeKeyAndVisible()
let nc = UINavigationController(rootViewController: ViewController())
window?.rootViewController = nc
return true
}
}
Initial state
Tapping on the SearchBar
The screen is obscured by the UITableView - colored in green
What could cause this bug and how can it be avoided?
Solved by adding this to the SearchResultsUpdater:
edgesForExtendedLayout = []
With default edges, the ResultsController tries to extend its view beyond the UINavigationBar and hence, obscures it:

Injecting an object into UIViewController from AppDelegate

I am attempting to inject an object into a UIViewController from the AppDelegate but I am not sure that I am doing it correctly. Please can someone advise. I get an error when I start my application at the line of code marked 'THE ERROR OCCURS HERE'.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Create ItemStore instance
let itemStoreObject = ItemStore()
let storyBoard: UIStoryboard = UIStoryboard.init(name: "Main", bundle: nil)
let testController = storyBoard.instantiateViewController(withIdentifier: "testTableController") as! TestTableViewController
testController.itemstore = itemStoreObject
return true
}
ItemStore:
import UIKit
class ItemStore {
var allItems = ["Thanh", "David", "Tommy", "Maria"]
}
TestTableViewController:
class TestTableViewController: UIViewController, UITableViewDelegate, UISearchBarDelegate, UITableViewDataSource{
#IBOutlet var myTableView: UITableView!
var itemstore: ItemStore!
override func viewDidLoad() {
super.viewDidLoad()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print("numberOfRowsSection ...")
return itemstore.allItems.count // THE ERROR OCCURS HERE.
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
print("cellForRow ...")
// Get a new or recycled cell
let cell = UITableViewCell(style: .value1, reuseIdentifier: "UITableViewCell")
let name = itemstore.allItems[indexPath.row]
cell.textLabel?.text = name
return cell
}
}
I get the following error message (marked in the line 'THE ERROR OCCURS HERE'):
fatal error: unexpectedly found nil while unwrapping an Optional value
(lldb)
You instantiate a view controller in AppDelegate, but the system will create another instance of that view controller class and hence display an instance of the class that has no itemstore property initialised.
You either have to make itemstore a type variable instead of an instance variable or if you only need this functionality for your root view controller, you have to instantiate the itemstore variable for your root view controller instance, which you know will be the one used by your navigation controller.

Pass Data between xib files without storyboard in Swift3

I am new to swift (and new to StackOverflow) and learning to use nib files now as I did quite a bit of learning with storyboard already.
In storyboard we can instantiate segues and unwindSegues to fetch data from one VC to another. How can I do the same WITHOUT using storyboard?
I currently have a scrollview(MainViewController.xib) that loads a tableview(ContactView.xib) on it. I have an add button overlaying the tableview which is currently unresponsive.
I want to be able to load another view (AddContact.xib) when I click on the add button to add a new data to my tableview and then unwind to reflect the new data in the table, but WITHOUT using storyboard.
I can provide my code if needed, but am only looking for someone to point me in the correct direction. I know this can be achieved using Navigation Controllers but I can't seem to find a fitting tutorial for it (most are old and use Obj-C. I am unfamiliar with Obj-C).
I even looked at some similar questions like: this and this
but they failed to answer my question.
Any help is appreciated. Thank you.
My AppDelegate:
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var mainVC: MainViewController? = nil
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
mainVC = MainViewController(nibName: "MainViewController", bundle: nil)
let frame = UIScreen.main.bounds
window = UIWindow(frame: frame)
window!.rootViewController = mainVC
window!.makeKeyAndVisible()
return true
}
MainViewController.swift:
class MainViewController: UIViewController {
#IBOutlet weak var scrollView: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let contactView: ContactView = ContactView(nibName: "ContactView", bundle: nil)
let dialerView: DialerView = DialerView(nibName: "DialerView", bundle: nil)
self.addChildViewController(dialerView)
self.scrollView.addSubview(dialerView.view)
dialerView.didMove(toParentViewController: self)
self.addChildViewController(contactView)
self.scrollView.addSubview(contactView.view)
contactView.didMove(toParentViewController: self)
var contactViewFrame : CGRect = contactView.view.frame
contactViewFrame.origin.x = self.view.frame.width
contactView.view.frame = contactViewFrame
self.scrollView.contentSize = CGSize(width: self.view.frame.width * 2, height: self.view.frame.height)
}
}
My ContactView.xib:
class ContactView: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var searchBar: UISearchBar!
var names = ["Rohan", "Rahul", "Sneh", "Paa", "Maa", "Vatsal", "Manmohan"]
var numbers = ["9830000001", "9830000002", "9830000003", "9830000004", "9830000005", "9830000006", "9830000007"]
let navVC: UINavigationController? = nil
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return names.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
tableView.rowHeight = CGFloat(60)
tableView.register(UINib(nibName: "ContactCell", bundle: nil), forCellReuseIdentifier: "Contact")
let cell = tableView.dequeueReusableCell(withIdentifier: "Contact", for: indexPath) as! ContactCell
cell.nameLabel.text = names[indexPath.row]
cell.numLabel.text = numbers[indexPath.row]
return cell
}
// Make delete-able in scroll view
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == UITableViewCellEditingStyle.delete {
names.remove(at: indexPath.row)
numbers.remove(at: indexPath.row)
tableView.reloadData()
}
}
My AddContact.xib only has the outlets defined.
Edit: I found a solution thanks to the commenters.
To Anyone wondering how I did it, I'll post the steps below:
Step 1: Create a protocol (I did so in my MainViewController.xib)
protocol NewContactDelegate {
func add_Contact (name: String, num: String)
}
Step 2: Instantiate a delegate for it in the senderVC. AddContact.xib was the senderVC for me
class AddContact: UIViewController {
var delegate: NewContactDelegate? = nil
#IBOutlet weak var nameField: UITextField!
#IBOutlet weak var numField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func saveContact(_ sender: UIButton) {
if delegate != nil {
if let name = nameField?.text, let num = numField?.text {
delegate?.add_Contact(name: name, num: num)
dismiss(animated: true, completion: nil)
}
}
}
Step 3: Make ReceivingVC conform to the delegate and accept data.
class ContactView: ....., NewContactDelegate {
func add_Contact(name: String, num: String) {
names.append(name)
numbers.append(num)
addressBook.reloadData() // This is my TableView outlet
}
}
**
NOTE: Remember to assert the delegate to a non-nil value in the RecievingVC to make sure #IBAction in SenderVC works as expected.
**

Resources