I am presenting an alert with UITextfield, but its delegate methods are not getting called. What wrong I might be doing. I am using the below code to show the alert with textfield.
func takePasscodeToEnableTouch(){
self.passcodeInputOperationType = .EnableTouchID
alertControllerPassCodeEntry = UIAlertController(title: "", message: "Enter Passcode to enable the Touch Id.", preferredStyle: UIAlertControllerStyle.Alert)
let cancelAction = UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Cancel) { (action) -> Void in
}
alertControllerPassCodeEntry!.addAction(cancelAction)
alertControllerPassCodeEntry!.addTextFieldWithConfigurationHandler { (txtField) -> Void in
txtField.placeholder = "Enter passcode"
txtField.delegate = self
txtField.tag = TextFieldTag.EnterPassCode
txtField.keyboardType = UIKeyboardType.NumbersAndPunctuation
txtField.accessibilityIdentifier = "PassCode"
txtField.secureTextEntry = true
txtField.addTarget(self, action:"textFieldDidChange:", forControlEvents: UIControlEvents.EditingChanged)
}
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.window?.rootViewController?.presentViewController(alertControllerPassCodeEntry!, animated: true, completion: nil )
}
And the textField delegate methods are :
func textFieldShouldBeginEditing(textField: UITextField) -> Bool
{
return true
}
func textFieldDidBeginEditing(textField: UITextField) // became first responder
{
}
func textFieldShouldEndEditing(textField: UITextField) -> Bool
{
return true
}
func textFieldDidEndEditing(textField: UITextField)
{
}
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool
{
var isLimitExist: Bool
var accessIndentifier: String
if let str = textField.accessibilityIdentifier
{
accessIndentifier = str
}
else
{
accessIndentifier = ""
}
//checkFieldLimit function is used to check the limit of text and restrict
isLimitExist = UIUtils.checkFieldLimit(accessIndentifier, stringToMatch: textField.text!, rangeLength: range.length, stringLength: string.characters.count)
if !isLimitExist
{
return false
}
return true
}
Ok, so with information from the comments, everything seems clear now. To recap, you call the method showing the alert like this :
#IBAction func swtchTouchAction(sender: UISwitch) {
if sender.on {
let passCodeManager = PasscodeManager()
passCodeManager.delegate = self
passCodeManager.takePasscodeToEnableTouch()
} else {
let passCodeManager = PasscodeManager()
passCodeManager.delegate = self
passCodeManager.authenticatePasscodeToDisalbeTouch()
}
}
Now, you don't retain (meaning - assign to a strong property) the passCodeManager anywhere in here. This means, that at the end of this method this object gets destroyed (thanks to ARC - Automatic Reference Counting). One may think that it would get retained because you assigned it as a delegate of the text field, but delegates are weak proeprties 99.99% of time - this means that they don't bump the retain count of objects assigned to them.
To solve your immediate issue you should make a property in your class in which you have swtchTouchAction method and change your code like this :
var passCodeManager: PasscodeManager?
#IBAction func swtchTouchAction(sender: UISwitch) {
if sender.on {
self.passCodeManager = PasscodeManager()
self.passCodeManager?.delegate = self
self.passCodeManager?.takePasscodeToEnableTouch()
} else {
self.passCodeManager = PasscodeManager()
self.passCodeManager?.delegate = self
self.passCodeManager?.authenticatePasscodeToDisalbeTouch()
}
}
This will be enough to retain your passcode manager.
I'd also suggest you read up on how memory management is done in Swift.
Related
My code for the alert is as follows:
struct AlertControlView: UIViewControllerRepresentable {
#Binding var textString: String
#Binding var showAlert: Bool
var title: String
var message: String
// Make sure that, this fuction returns UIViewController, instead of UIAlertController.
// Because UIAlertController gets presented on UIViewController
func makeUIViewController(context: UIViewControllerRepresentableContext<AlertControlView>) -> UIViewController {
return UIViewController() // Container on which UIAlertContoller presents
}
func updateUIViewController(_ uiViewController: UIViewController, context: UIViewControllerRepresentableContext<AlertControlView>) {
// Make sure that Alert instance exist after View's body get re-rendered
guard context.coordinator.alert == nil else { return }
if self.showAlert {
// Create UIAlertController instance that is gonna present on UIViewController
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
context.coordinator.alert = alert
// Adds UITextField & make sure that coordinator is delegate to UITextField.
alert.addTextField { textField in
textField.placeholder = "Enter some text"
textField.text = self.textString // setting initial value
textField.delegate = context.coordinator // using coordinator as delegate
}
// As usual adding actions
alert.addAction(UIAlertAction(title: NSLocalizedString("Cancel", comment: "") , style: .destructive) { _ in
// On dismiss, SiwftUI view's two-way binding variable must be update (setting false) means, remove Alert's View from UI
alert.dismiss(animated: true) {
self.showAlert = false
}
})
alert.addAction(UIAlertAction(title: NSLocalizedString("Submit", comment: ""), style: .default) { _ in
// On submit action, get texts from TextField & set it on SwiftUI View's two-way binding varaible `textString` so that View receives enter response.
if let textField = alert.textFields?.first, let text = textField.text {
self.textString = text
}
alert.dismiss(animated: true) {
self.showAlert = false
}
})
// Most important, must be dispatched on Main thread,
// Curious? then remove `DispatchQueue.main.async` & find out yourself, Dont be lazy
DispatchQueue.main.async { // must be async !!
uiViewController.present(alert, animated: true, completion: {
self.showAlert = false // hide holder after alert dismiss
context.coordinator.alert = nil
})
}
}
}
func makeCoordinator() -> AlertControlView.Coordinator {
Coordinator(self)
}
class Coordinator: NSObject, UITextFieldDelegate {
// Holds reference of UIAlertController, so that when `body` of view gets re-rendered so that Alert should not disappear
var alert: UIAlertController?
// Holds back reference to SwiftUI's View
var control: AlertControlView
init(_ control: AlertControlView) {
self.control = control
}
func textFieldDidEndEditing(_ textField: UITextField) {
textField.text = textField.text!.replacingOccurrences(of: "^0+", with: "")
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if let text = textField.text as NSString? {
self.control.textString = text.replacingCharacters(in: range, with: string)
} else {
self.control.textString = ""
}
return true
}
}
}
Its being called as such:
#State var changePassword = false
var body: some View {
ZStack {
HStack {
Text("Password")
Spacer()
Text(String(editingPool.password))
.foregroundColor(.gray)
.font(.callout)
Button(action: {
changePassword.toggle()
}) {
Text("Change")
}
}
if self.changePassword {
AlertControlView(textString: $editingPool.password,
showAlert: $changePassword,
title: "Change Pool Password",
message: "Enter the new pool password.")
}
}
}
I cant seem to get the shouldChangeCharactersIn function to get called, or any of the begin, end or onchange functions either?
I have a problem that I can't override function.
My parent class below:
class A: UIViewController {
func parentalGate() {
let appearance = SCLAlertView.SCLAppearance(
kTitleFont: UIFont(name: "Futura", size: 20)!,
kTextFont: UIFont(name: "Futura", size: 14)!,
kButtonFont: UIFont(name: "Futura", size: 14)!,
showCloseButton: false
)
let alert = SCLAlertView(appearance: appearance)
let alert2 = SCLAlertView(appearance: appearance)
if (UserDefaults.standard.object(forKey: "language") as? String == "english") {
let txt = alert.addTextField("Enter third letter")
alert.addButton("Done") {
if ((txt.text == "P") || (txt.text == "p")) {
self.parentalGatefunctions()
} else {
alert2.addButton("Close", target: SCLAlertView(), selector: #selector(SCLAlertView.hideView))
alert2.showError("Incorrect letter entered", subTitle: "Try again")
}
}
alert.addButton("Close", target: SCLAlertView(), selector: #selector(SCLAlertView.hideView))
alert.showWarning("We need to be sure that You are an adult", subTitle: "Enter the third letter of word: HAPPY")
}
}
func parentalGatefunctions(){
// Parental gate functions are overriten in classes where they are used for
}
}
Child class below:
class B: A {
#IBAction func unlockAllCategoriesBtnPressed(_ sender: Any) {
A().parentalGate()
}
override func parentalGatefunctions() {
print ("Do something in class B")
}
}
Another class:
class C: A {
#IBAction func unlockAllCategoriesBtnPressed(_ sender: Any) {
A().parentalGate()
}
override func parentalGatefunctions() {
print ("Do something in class C")
}
}
All the time when parentalGate() method is called in classes B and C only empty parentalGateFunctions() is called without the overriten methods are not called in classes.
Am I doing something wrong? I just want to avoid repetitive code in my classes.
Thanks!
Your mistake is here.
#IBAction func unlockAllCategoriesBtnPressed(_ sender: Any) {
A().parentalGate()
}
Since you already have subclassed A, that method is available to you. So, just call the method in your subclass rather than the one in a new instance of the superclass! (which would obviously call the empty method)
#IBAction func unlockAllCategoriesBtnPressed(_ sender: Any) {
parentalGate()
}
I am trying to input information in a textfield and then store the information that is typed out as a variable, to be used in a string. For some reason it crashes whenever I click on the next textfield after the first, or the third after the second. Here is what I have.
var name: String?
#IBAction func nameField(name: UITextField) {
let name = name.text
}
var contact: String?
#IBAction func contactField(contact: UITextField) {
let contact = contact.text
}
var other: String = "nothing"
#IBAction func otherField(other: UITextField) {
if other == nil {
}else{
let other = other.text!
}
}
override func shouldPerformSegueWithIdentifier(identifier: String, sender: AnyObject!) -> Bool {
let appearance = SCLAlertView.SCLAppearance(
showCloseButton: false,
showCircularIcon: false
)
let alertView = SCLAlertView(appearance: appearance)
alertView.addButton("First") {
return true
}
alertView.addButton("Second") {
return false
}
alertView.showSuccess("warning", subTitle: "something")
}
This code above wants a return true/false before the last curly bracket but if I do that I lose the button return.
So is there any way to archive this?
I use SCLAlertView for my alert (if someone knows how to do it with uialert I would like to see it)
Thanks in advance.
to add to the first comment, I did it like this with the use of a UISwitch in a header cell with a bool variable declared in the top of the class
#IBAction func `switchAction`(_ sender: UISwitch) {
selected = sender.isOn
if selected == false {
//do you stuff here change false to true if you need it that way
}
}
then in this method setup like this
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
var enable: Bool = false
if selected == false {
enable = true
} else {
enable = false
}
return enable
}
you can use block- closesure
var alertAction :((result:Bool)->())?
use it :
alertView.addButton("Second") {
alertAction?(false)
}
implement:
alertAction = {result in
// do s.t
}
I want to improve the MPCRevisited project which is Chat app that using multi peer method. I'm using BLE to connect one device to another device (iPad and iPod) and send and receive the data. However, when I press home button to make background mode on one device, after 5 seconds, I can't send or receive the data.
image description here
I've already check all the thing in background modes, but still its not working at all.
import UIKit
import MultipeerConnectivity
class ParkBenchTimer {
let startTime:CFAbsoluteTime
var endTime:CFAbsoluteTime?
init() {
startTime = CFAbsoluteTimeGetCurrent()
}
func stop() -> CFAbsoluteTime {
endTime = CFAbsoluteTimeGetCurrent()
return duration!
}
var duration:CFAbsoluteTime? {
if let endTime = endTime {
return endTime - startTime
} else {
return nil
}
}
}
class ChatViewController: UIViewController, UITextFieldDelegate, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var chatTextField: UITextField!
#IBOutlet weak var chatTableView: UITableView!
var messagesArray: [[String : String]] = []
let mpcManager = MPCManager.sharedInstance
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.chatTableView.delegate = self
self.chatTableView.dataSource = self
self.chatTableView.estimatedRowHeight = 60.0
self.chatTableView.rowHeight = UITableViewAutomaticDimension
self.chatTextField.delegate = self
self.mpcManager.messageRecievedDelegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
// MARK: IBAction method implementation
#IBAction func endChat(sender: AnyObject) {
let messageDictionary: [String: String] = ["message": "_end_chat_"]
if self.mpcManager.sendData(dictionaryWithData: messageDictionary, toPeer: self.mpcManager.session.connectedPeers[0] as MCPeerID){
self.dismissViewControllerAnimated(true, completion: { () -> Void in
self.mpcManager.session.disconnect()
})
}
}
// MARK: UITableView related method implementation
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.messagesArray.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCellWithIdentifier("idCell") else {
assert(true)
return UITableViewCell()
}
guard let currentMessage = self.messagesArray[safe: indexPath.row] else {
print(" ")
assert(true)
return UITableViewCell()
}
if let sender = currentMessage["sender"] {
var senderLabelText: String
var senderColor: UIColor
if sender == "self" {
senderLabelText = "I said:"
senderColor = UIColor.purpleColor()
} else {
senderLabelText = sender + " said:"
senderColor = UIColor.orangeColor()
}
cell.detailTextLabel?.text = senderLabelText
cell.detailTextLabel?.textColor = senderColor
}
if let message = currentMessage["message"] {
cell.textLabel?.text = message
}
return cell
}
// MARK: UITextFieldDelegate method implementation
func textFieldShouldReturn(textField: UITextField) -> Bool {
textField.resignFirstResponder()
guard let textFieldText = textField.text else {
assert(true)
return false
}
let messageDictionary: [String: String] = ["message": textFieldText]
guard let connectedPeer = self.mpcManager.session.connectedPeers[safe: 0] else {
print(" ")
assert(true)
return false
}
if self.mpcManager.sendData(dictionaryWithData: messageDictionary, toPeer: connectedPeer) {
let dictionary = ["sender": "self", "message": textFieldText]
self.messagesArray.append(dictionary)
self.updateTableview()
} else {
print("Could not send data")
}
textField.text = ""
return true
}
// MARK: Custom method implementation
func updateTableview(){
chatTableView.reloadData()
if self.chatTableView.contentSize.height > self.chatTableView.frame.size.height {
let indexPathToScrollTo = NSIndexPath(forRow: messagesArray.count - 1, inSection: 0)
self.chatTableView.scrollToRowAtIndexPath(indexPathToScrollTo, atScrollPosition: .Bottom, animated: true)
}
}
}
extension ChatViewController : MPCManagerRecievedMessageDelegate {
func managerRecievedData(data:NSData ,fromPeer:MCPeerID) {
// Convert the data (NSData) into a Dictionary object.
let dataDictionary = NSKeyedUnarchiver.unarchiveObjectWithData(data) as! [String : String]
// Check if there's an entry with the "message" key.
if let message = dataDictionary["message"] {
// Make sure that the message is other than "_end_chat_".
if message != "_end_chat_"{
// Create a new dictionary and set the sender and the received message to it.
let messageDictionary: [String: String] = ["sender": fromPeer.displayName, "message": message]
// Add this dictionary to the messagesArray array.
messagesArray.append(messageDictionary)
// Reload the tableview data and scroll to the bottom using the main thread.
self.updateTableview()
} else {
}
}
}
func managerDidRecievedMessage(message: String, fromPeer: MCPeerID) {
// Create a new dictionary and set the sender and the received message to it.
//let messageDictionary: [String: String] = ["sender": fromPeer.displayName, "message": message]
// Add this dictionary to the messagesArray array.
//messagesArray.append(messageDictionary)
// Reload the tableview data and scroll to the bottom using the main thread.
//self.updateTableview()
}
func managerDidEndChat(fromPeer:MCPeerID) {
// In this case an "_end_chat_" message was received.
// Show an alert view to the user.
let alert = UIAlertController(title: "", message: "\(fromPeer.displayName) ended this chat.", preferredStyle: UIAlertControllerStyle.Alert)
let doneAction: UIAlertAction = UIAlertAction(title: "Okay", style: UIAlertActionStyle.Default) { (alertAction) -> Void in
self.mpcManager.session.disconnect()
self.dismissViewControllerAnimated(true, completion: nil)
}
alert.addAction(doneAction)
self.presentViewController(alert, animated: true, completion: nil)
}
}
This is my code.
Please help me if someone knows this problem. What I want to do is one device to keep sending the message and other device to become background and foreground back and forth.
Thank you.
Looking at some other StackOverflow posts (here and here), it seems like the Multipeer Connectivity Framework is not built to function in the background and your functionality will disappear after a couple minutes.
Bluetooth will function in the background, with the capabilities that you checked, but you will have to create your own messaging platform; even though Multipeer relies partially on Bluetooth, the capabilities are separate entities.