How to programmatically save the content written in TextView to a text file when terminating the app? - swift

I want to save the content of the text view when the user closes the app.
I used the following codes to do so, but I cannot get the up-to-date string of the textview when closing the app. So, the produced text file is blank.
How should I access to the NSTextView from AppDelegate to save its content?
ViewController.swift
import Cocoa
class ViewController: NSViewController {
static var textViewString: String = ""
#IBOutlet var textView: NSTextView!{
didSet{
ViewController.textViewString = textView.string
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
// start with hidden and show after moving to the main screen
DispatchQueue.main.async {
//keep the window top
self.view.window?.level = .floating
//set up the main display as the display where window shows up
let screens = NSScreen.screens
var pos = NSPoint()
pos.x = screens[0].visibleFrame.midX
pos.y = screens[0].visibleFrame.midY
self.view.window?.setFrameOrigin(pos)
self.view.window?.zoom(self)
self.view.window?.level = .floating
//self.view.window?.backgroundColor = NSColor.white
//stop the user from moving window
self.view.window?.isMovable = false
//disable resizable mode
self.view.window?.styleMask.remove(.resizable)
self.view.window?.setIsVisible(true)
}
//set up font for the reflectionForm
textView.font = NSFont.systemFont(ofSize: 30)
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
func saveTextViewString(){
if let documentDirectoryFileURL = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true).last {
let fileName = "savedText.txt"
let targetTextFilePath = documentDirectoryFileURL + "/" + fileName
do {
try ViewController.textViewString.write(toFile: targetTextFilePath, atomically: true, encoding: String.Encoding.utf8)
print("successfully recorded: \(ViewController.textViewString.description) at \(fileName.utf8CString)")
} catch let error as NSError {
print("failed to write: \(error)")
}
}
}
}
AppDelegate.swift
import Cocoa
#main
class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(_ aNotification: Notification) {
// Insert code here to initialize your application
}
func applicationWillTerminate(_ aNotification: Notification) {
// Insert code here to tear down your application
//save the string in the textview into a text file
ViewController().saveTextViewString()
}
func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool {
return true
}
}

Thank you for the #jnpdx's comments, I was able to solve this by just declaring ViewController in the AppDelegate by stating var viewController: ViewController!
ViewController.swift
import Cocoa
class ViewController: NSViewController {
#IBOutlet var textView: NSTextView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
// start with hidden and show after moving to the main screen
DispatchQueue.main.async {
//keep the window top
self.view.window?.level = .floating
//set up the main display as the display where window shows up
let screens = NSScreen.screens
var pos = NSPoint()
pos.x = screens[0].visibleFrame.midX
pos.y = screens[0].visibleFrame.midY
self.view.window?.setFrameOrigin(pos)
self.view.window?.zoom(self)
self.view.window?.level = .floating
//self.view.window?.backgroundColor = NSColor.white
//stop the user from moving window
self.view.window?.isMovable = false
//disable resizable mode
self.view.window?.styleMask.remove(.resizable)
self.view.window?.setIsVisible(true)
}
//set up font for the reflectionForm
textView.font = NSFont.systemFont(ofSize: 30)
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
func saveTextViewString(){
if let documentDirectoryFileURL = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true).last {
let fileName = "savedText.txt"
let targetTextFilePath = documentDirectoryFileURL + "/" + fileName
do {
try textView.string.write(toFile: targetTextFilePath, atomically: true, encoding: String.Encoding.utf8)
print("successfully recorded: \(textView.string.description) at \(fileName.utf8CString)")
} catch let error as NSError {
print("failed to write: \(error)")
}
}
}
}
AppDelegate.swift
import Cocoa
#main
class AppDelegate: NSObject, NSApplicationDelegate {
//connect viewController with ViewController
var viewController: ViewController!
func applicationDidFinishLaunching(_ aNotification: Notification) {
// Insert code here to initialize your application
}
func applicationWillTerminate(_ aNotification: Notification) {
// Insert code here to tear down your application
//save the string in the textview into a text file
viewController.saveTextViewString()
}
func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool {
return true
}
}

Related

Presenting view controller from detached view controller is discouraged. Keeping User logged in issue

I'm trying to have the user move to automatically go to the Home Screen and not have to log in again. Basically, to remember the user. I used User Defaults to save the user login info and put the listener for the key in the viewDidLoad of the first login page. I used an if statement to switch the view controllers but it doesn't work and prints (Presenting view controller from detached view controller is discouraged).
LoginViewController:
import UIKit
import FirebaseAuth
import AVKit
class LoginViewController: UIViewController {
var videoPlayer:AVPlayer?
var videoPlayerLayer:AVPlayerLayer?
#IBOutlet weak var emailTextField: UITextField!
#IBOutlet weak var Back: UIButton!
#IBOutlet weak var passwordtextField: UITextField!
#IBOutlet weak var loginButton: UIButton!
#IBOutlet weak var errorLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
setupElements()
}
func dismissKeyboard() {
//Causes the view (or one of its embedded text fields) to resign the first responder status.
view.endEditing(true)
}
func setupElements(){
errorLabel.alpha = 0
Utilities.styleTextField(emailTextField)
Utilities.styleTextField(passwordtextField)
Utilities.styleFilledButton(loginButton)
}
func validateFields() -> String?
{
//make sure fields are filled
if emailTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines) == "" || passwordtextField.text?.trimmingCharacters(in: .whitespacesAndNewlines) == ""
{
return "Please fill all fields"
}
return nil
}
#IBAction func loginTapped(_ sender: Any) {
//creates a clean version of the text field
let email = emailTextField.text!.trimmingCharacters(in: .whitespacesAndNewlines)
let password = passwordtextField.text!.trimmingCharacters(in: .whitespacesAndNewlines)
let error = validateFields()
//sign in user
Auth.auth().signIn(withEmail: email, password: password) { (result, error) in
UserDefaults.standard.set(Auth.auth().currentUser!.uid, forKey: "user_uid_key")
UserDefaults.standard.synchronize()
if error != nil{
self.errorLabel.text = "Invalid Username/Password try again."
self.errorLabel.alpha = 1
}
else{
let homeViewController = self.storyboard?.instantiateViewController(identifier: Constants.StoryBoard.homeViewController) as?
HomeViewController
self.view.window?.rootViewController = homeViewController
self.view.window?.makeKeyAndVisible()
}
}
//make sure all fields are filled
}
override func viewWillAppear(_ animated: Bool) {
setUpVideo()
}
func setUpVideo(){
//Get path to resource bundle
let bundlePath = Bundle.main.path(forResource: "IMG_7211 2", ofType: "mov")
guard bundlePath != nil else{
return
}
//create the url from it
let url = URL(fileURLWithPath: bundlePath!)
//Create The video Player item
let item = AVPlayerItem(url: url)
//create the player
videoPlayer = AVPlayer(playerItem: item)
//create the layer
videoPlayerLayer = AVPlayerLayer(player: videoPlayer!)
//adjust the size and frame
videoPlayerLayer?.frame = CGRect(x: -self.view.frame.size.width*1.5, y:0, width: self.view.frame.size.width*4, height: self.view.frame.size.height)
view.layer.insertSublayer(videoPlayerLayer!, at: 0)
//add and play
videoPlayer?.playImmediately(atRate: 0.8)
}
}
ViewController:
import UIKit
import AVKit
import Firebase
import FirebaseAuth
class ViewController: UIViewController {
var videoPlayer:AVPlayer?
var videoPlayerLayer:AVPlayerLayer?
#IBOutlet weak var signUpButton: UIButton!
#IBOutlet weak var logInButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
if UserDefaults.standard.object(forKey: "user_uid_key") != nil {
print("i see u")
let navController = UINavigationController(rootViewController: HomeViewController())
navController.navigationBar.barStyle = .black
self.present(navController, animated: false, completion: nil)
}
else {
let homeViewController = self.storyboard?.instantiateViewController(identifier: Constants.StoryBoard.homeViewController) as?
ViewController
self.view.window?.rootViewController = homeViewController
self.view.window?.makeKeyAndVisible()
}
// Do any additional setup after loading the view.
setupElements()
}
func showhomepage() {
let homeViewController = self.storyboard?.instantiateViewController(identifier: Constants.StoryBoard.homeViewController) as?
HomeViewController
self.view.window?.rootViewController = homeViewController
self.view.window?.makeKeyAndVisible()
}
override func viewWillAppear(_ animated: Bool) {
//Set up video in background
setUpVideo()
}
func setUpVideo(){
//Get path to resource bundle
let bundlePath = Bundle.main.path(forResource: "Project", ofType: "mp4")
guard bundlePath != nil else{
return
}
//create the url from it
let url = URL(fileURLWithPath: bundlePath!)
//Create The video Player item
let item = AVPlayerItem(url: url)
//create the player
videoPlayer = AVPlayer(playerItem: item)
//create the layer
videoPlayerLayer = AVPlayerLayer(player: videoPlayer!)
//adjust the size and frame
videoPlayerLayer?.frame = CGRect(x: -self.view.frame.size.width*1.5, y:0, width: self.view.frame.size.width*4, height: self.view.frame.size.height)
view.layer.insertSublayer(videoPlayerLayer!, at: 0)
//add and play
videoPlayer?.playImmediately(atRate: 1)
}
func setupElements(){
Utilities.styleFilledButton(signUpButton)
Utilities.styleHollowButton(logInButton)
}
}
Looks like you're using Firebase. Do not store any login information in the User defaults. What you should do is create a blank view controller that will check if the user is signed in. If the user is signed it, it will present your HomeViewController; if the user is not signed in, it will present the login screen. You can also choose to perform these checks in your AppDelegate/SceneDelegate if you want to avoid the extra view controller.
The empty ViewController should be the initial/root ViewController.
You cannot present view controllers from inside viewDidLoad, use viewDidAppear.
Here is a basic example for the view controller way:
// in the new empty view controller, import FirebaseAuth
var handle: AuthStateDidChangeListenerHandle!
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(false)
handle = Auth.auth().addStateDidChangeListener { auth, user in
if user != nil {
// Go to Home Screen/ switch root
} else {
// Go to sign in screen/ switch root
}
}
}

Problem saving text from UITextView into Realm (Swift 5)

Trying to save the user's textView.text into Realm. Figured if I call my save function into the textFieldDidEndEditing function, it would trigger my Realm database to save it. Function runs ok (prints "Saved Successfully" when I end editing), but when I close out and come back, none of the data is there.
import UIKit
import RealmSwift
class NoteViewController: UIViewController, UITextViewDelegate {
var textView = UITextView()
var notes: Results<Notes>?
let realm = try! Realm()
var selectedNote: Menu? {
didSet {
loadNotes()
}
}
#IBOutlet weak var theTextView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
loadNotes()
self.textView.delegate = self
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.view.endEditing(true)
}
override func viewDidDisappear(_ animated: Bool) {
saveNote()
}
func textViewDidBeginEditing(_ textView: UITextView) {
}
func textViewDidEndEditing(_ textView: UITextView) {
saveNote()
}
//MARK: - Data Manipulation
func loadNotes() {
notes = realm.objects(Notes.self)
textView.reloadInputViews()
}
func saveNote() {
if let currentNote = self.selectedNote {
do {
try self.realm.write {
let newNote = Notes()
newNote.body = theTextView.text!
newNote.dateCreated = Date()
currentNote.notes.append(newNote)
print("Saved successfully")
}
} catch {
print("Error saving note body, with \(error)")
}
}
}
}
Realm File:
class Notes: Object {
#objc dynamic var body: String = ""
#objc dynamic var dateCreated: Date?
var parent = LinkingObjects(fromType: Menu.self, property: "notes")
}
Menu Realm File:
class Menu: Object {
#objc dynamic var name: String = ""
#objc dynamic var preview: String = ""
let notes = List<Notes>()
}
'''
It looks like you are using a storyboard to create your layout.
Get rid of var textView = UITextView() and textView.reloadInputViews() then.
Replace self.textView.delegate = self with theTextView.delegate = self in viewDidLoad().
To check the content of theTextView modify your print statement:
print("Saved successfully: \(theTextView.text!)")
If there is no text printed right after "Saved successfully: " then check Interface builder for theTextView: verify the outlet of theTextView and that this is actually the UITextView you are using (since your app does print something it looks like it is hooked up somehow because otherwise it would crash in newNote.body = theTextView.text!).
I think you also forgot to actually initialise theTextView with notes.body.
Try to modify loadNotes() like this:
func loadNotes() {
notes = realm.objects(Notes.self)
theTextView.text = notes.body
}

Change search field's icon

I try to implement search behavior like in Xcode: if you enter something in search field, icon changes color.
I delegate both searchFieldDidStartSearching and searchFieldDidEndSearching to controller and change the image.
The problem is icon's image changes only when window lose it's focus.
class ViewController: NSViewController {
#IBOutlet weak var searchField: NSSearchField!
func searchFieldDidStartSearching(_ sender: NSSearchField) {
print("\(#function)")
(searchField.cell as! NSSearchFieldCell).searchButtonCell?.image = NSImage.init(named: "NSActionTemplate")
}
func searchFieldDidEndSearching(_ sender: NSSearchField) {
print("\(#function)")
(searchField.cell as! NSSearchFieldCell).searchButtonCell?.image = NSImage.init(named: "NSHomeTemplate")
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
}
Thanks in advance for any ideas/suggestions.
Although I don't know the reason, it works:
NSApp.mainWindow?.resignMain()
NSApp.mainWindow?.becomeMain()
Here is the whole code:
class MyViewController: NSViewController {
private lazy var searchField: NSSearchField = {
let searchField = NSSearchField(string: "")
if let searchButtonCell = searchField.searchButtonCell {
searchButtonCell.setButtonType(.toggle)
let filterImage = #imageLiteral(resourceName: "filter")
searchButtonCell.image = filterImage.tinted(with: .systemGray)
searchButtonCell.alternateImage = filterImage.tinted(with: .systemBlue)
}
searchField.focusRingType = .none
searchField.bezelStyle = .roundedBezel
searchField.delegate = self
return searchField
}()
...
}
extension MyViewController: NSSearchFieldDelegate {
func searchFieldDidStartSearching(_ sender: NSSearchField) {
sender.searchable = true
}
func searchFieldDidEndSearching(_ sender: NSSearchField) {
sender.searchable = false
}
}
extension NSSearchField {
var searchButtonCell: NSButtonCell? {
(self.cell as? NSSearchFieldCell)?.searchButtonCell
}
var searchable: Bool {
get {
self.searchButtonCell?.state == .on
}
set {
self.searchButtonCell?.state = newValue ? .on : .off
self.refreshSearchIcon()
}
}
private func refreshSearchIcon() {
NSApp.mainWindow?.resignMain()
NSApp.mainWindow?.becomeMain()
}
}
extension NSImage {
func tinted(with color: NSColor) -> NSImage? {
guard let image = self.copy() as? NSImage else { return nil }
image.lockFocus()
color.set()
NSRect(origin: NSZeroPoint, size: self.size).fill(using: .sourceAtop)
image.unlockFocus()
image.isTemplate = false
return image
}
}
I was having the same issue. A simple override fixed this issue for me
extension NSSearchField{
open override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
}
}
As you can see when you click inside the view it's still focussed on the search text field(as you can still type in it after you clicked underneath it). Since the change image is on when it loses focus, you should check if you clicked outside of the text field.
Solve problem by subclassing NSSearchFieldCell and assign this class to field's cell.
You don't even need to subclass NSSearchFieldCell.
When you create your NSSearchField from code, you can do something like this:
if let searchFieldCell = searchField.cell as? NSSearchFieldCell {
let image = NSImage(named: "YourImageName")
searchFieldCell.searchButtonCell?.image = image
searchFieldCell.searchButtonCell?.alternateImage = image // Optionally
}
If you're using storyboards, you can do the same in didSet of your #IBOutlet.

How to implement custom camera preview in iOS?

I am trying to implement Custom camera effect like:- Image
I Thought that this is achieve like this way
This type of functionality already implemented in one app which is available in App Store. here is the link enter link description here
I want to copy this app's camera functionality.
I have already implemented something like this.
I am using below code for achieved above functionality.
Into ViewController.swift class.
import UIKit
import AVFoundation
#available(iOS 10.0, *)
class ViewController: UIViewController
{
#IBOutlet weak var vc: UIView!
#IBOutlet weak var img: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
setupCamera()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
session.startRunning()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
session.stopRunning()
}
#IBOutlet fileprivate var previewView: PreviewView! {
didSet {
previewView.videoPreviewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
previewView.layer.cornerRadius = previewView.layer.frame.size.width/2
previewView.clipsToBounds = true
}
}
#IBOutlet fileprivate var imageView: UIImageView! {
didSet {
imageView.layer.cornerRadius = imageView.layer.frame.size.width/2
imageView.clipsToBounds = true
}
}
fileprivate let session: AVCaptureSession = {
let session = AVCaptureSession()
session.sessionPreset = AVCaptureSessionPresetPhoto
return session
}()
fileprivate let output = AVCaptureStillImageOutput()
}
#available(iOS 10.0, *)
extension ViewController {
func setupCamera() {
let backCamera = AVCaptureDevice.defaultDevice(withMediaType:
AVMediaTypeVideo)
guard let input = try? AVCaptureDeviceInput(device: backCamera) else {
fatalError("back camera not functional.") }
session.addInput(input)
session.addOutput(output)
previewView.session = session
}
}
// MARK: - #IBActions
#available(iOS 10.0, *)
private extension ViewController {
#IBAction func capturePhoto() {
if let videoConnection = output.connection(withMediaType: AVMediaTypeVideo) {
output.captureStillImageAsynchronously(from: videoConnection, completionHandler: { (CMSampleBuffer, Error) in
if let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(CMSampleBuffer) {
if let cameraImage = UIImage(data: imageData) {
self.imageView.image = cameraImage
UIImageWriteToSavedPhotosAlbum(cameraImage, nil, nil, nil)
}
}
})
}
}
}
Also Create Preview Class and this class into UIView from storyboard file.
From above code I have achived this image.
I need to add any shape of image layer as a frame into UIView. ButI have no idea how to achieved this type of functionality.
So, Basically my task is, how to add any shape of image layer into UIView and after capture image how to save image with image layer, like Final Image clue image

Changing the view color when comparing values

I created a view to use as background and I would like to change its color when label text is greater or less than variable number. The script is okay but the color is not changing.
Thanks in advance.
import UIKit
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var localName: UITextField!
#IBOutlet weak var localNameLabel: UILabel!
#IBOutlet weak var localTemp: UILabel!
#IBAction func getData(sender: AnyObject) {
getWeatherData("http://api.openweathermap.org/data/2.5/weather?q=" + localName.text! + "")
}
#IBOutlet weak var fundo: UIView!
override func viewDidLoad() {
super.viewDidLoad()
getWeatherData("http://api.openweathermap.org/data/2.5/weather?q=London")
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func getWeatherData(urlString: String){
let url = NSURL (string: urlString)
let task = NSURLSession.sharedSession().dataTaskWithURL(url!) { (data, response, error) in
dispatch_async(dispatch_get_main_queue(), {
self.setLabels(data!)
})
}
task.resume()
}
func setLabels(weatherData: NSData) {
do {
let json = try NSJSONSerialization.JSONObjectWithData(weatherData, options:NSJSONReadingOptions.MutableContainers) as! NSDictionary
print(json)
//localNameLabel.text = json[("name")] as? String
if let name = json[("name")] as? String {
localNameLabel.text = name
}
if let main = json[("main")] as? NSDictionary {
if let temp = main[("temp")] as? Double {
//convert kelvin to celsius
let ft = (temp - 273.15)
let myString = ft.description
localTemp.text = myString
self.changeColor()
}
}
} catch let error as NSError {
print(error)
}
var number : Float
func changeColor(){
number = 19.0
if(Float(localTemp.text!) < number){
fundo.backgroundColor = .blueColor()
}else{
fundo.backgroundColor = .orangeColor()
}
}
}
}
Edited to post the entire script
In your view controller you need to add UITextFieldDelegate which will allow you to access methods related to your text field. The top of your view controller should look like this:
class ViewController: UIViewController,UITextFieldDelegate //set delegate to class
You then need to set the delegate of your text field to self in viewDidLoad and add a target for when the text field changes:
override func viewDidLoad() {
super.viewDidLoad()
localTemp.delegate = self //set delegate to this vc
localTemp.addTarget(self, action: "textFieldDidChange:", forControlEvents: UIControlEvents.EditingChanged)
}
You can then implement this method which will run on every key press and you need to call your changeColor() method as above:
func textFieldDidChange(textField: UITextField) {
self.changeColor()
}