Hi I am new for developing ios application.I have used some cocopods framework for doing the alert view. I have used alert view following mentioned sweetAlert. In that I have tried to dismiss the alert programmatically with out press the tab button in alert.Please any one help for fix the problem.
You can dismiss the alert by calling dismissViewControllerAnimated method on alertController object.
alertControllerObject?.dismissViewControllerAnimated(true, completion: nil)
I think you can use pressed(sender: UIButton!) method in SweetAlert class.
#IBAction func aBasicMessageAlert(sender: AnyObject) {
let sweetAlert = SweetAlert().showAlert("Here's a message!")
close(sweetAlert, after: 2.0)
}
func close(alert: SweetAlert, after seconds: Double) {
NSTimer.scheduledTimerWithTimeInterval(seconds,
target: self,
selector: #selector(closeAlert),
userInfo: ["alert": alert],
repeats: true)
}
func closeAlert(timer: NSTimer) {
let alert = timer.userInfo!["alert"] as! SweetAlert
let dummyCloseButton = UIButton()
dummyCloseButton.tag = 0
alert.pressed(dummyCloseButton)
}
Use this,
yourAlerView.dismiss(withClickedButtonIndex: 0, animated: true)
when you handle the return key.
ButtonIndex is index of button you want to click by default to hide alert.
Hope this will help you.
Try This
When you call Alert Method Also Called this, Inside the alert Method
NSTimer.scheduledTimerWithTimeInterval(0.5, target: self, selector: "hideAlert:", userInfo: userInfo, repeats: true) //repeats: false
Called This outside the Alert Method
func hideAlert(){
isOtherButton == true// isOtherButton getting from your SweetAlert Demo
}
You need to add this method in SweetAlert, and call it.
func closeAlert(){
UIView.animateWithDuration(0.5, delay: 0.0, options: UIViewAnimationOptions.CurveEaseOut, animations: { () -> Void in
self.view.alpha = 0.0
}) { (Bool) -> Void in
self.view.removeFromSuperview()
self.cleanUpAlert()
//Releasing strong refrence of itself.
self.strongSelf = nil
}
}
Implement alert like this
let alert = SweetAlert() // take this as global
func showAlert(){
alert.showAlert(title as String, subTitle: msg as String, style: style, buttonTitle:buttonOtherTitle as String, buttonColor:UIColor.redColor() , otherButtonTitle: buttonOkTitle as String, otherButtonColor: colors.KBlueTextColor!) { (isOtherButton) -> Void in
if isOtherButton
{
completionHandler(false)
}
else
{
completionHandler(true)
}
}
}
func CloseAlert(){
alert.closeAlert()
}
Related
Can we customize SwiftEventBus Library to only trigger in the current active ViewController.
I'm trying to trigger an action when ever a notification occurs, so i'm using swift event bus to trigger when ever a push notification comes but it is triggering in all the places it is registered. Can we make so that it will only trigger the action in the active view. If not is there any other library I can use?
Wouldn't it be enough to deregister inactive ViewControllers as mentioned in the SwiftEventBus readme?
//Perhaps on viewDidDisappear depending on your needs
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
SwiftEventBus.unregister(self)
}
Modify library(or subclass SwiftEventBus) like below:
#discardableResult
open class func on(_ target: AnyObject, name: String, sender: Any? = nil, queue: OperationQueue?, handler: #escaping ((Notification?) -> Void)) -> NSObjectProtocol {
let id = UInt(bitPattern: ObjectIdentifier(target))
//modification start
let handlerIner:((Notification?) -> Void) = { [weak target] n in
if let vc = target as? UIViewController, vc.view?.window != nil {
handler(n)
}
}
let observer = NotificationCenter.default.addObserver(forName: NSNotification.Name(rawValue: name), object: sender, queue: queue, using: handlerIner)
// modification end
let namedObserver = NamedObserver(observer: observer, name: name)
Static.queue.sync {
if let namedObservers = Static.instance.cache[id] {
Static.instance.cache[id] = namedObservers + [namedObserver]
} else {
Static.instance.cache[id] = [namedObserver]
}
}
return observer
}
I'm implementing In-App purchases in an XCode project, and everything works fine except for one error. When the user isn't connected to the internet and he clicks a purchase button, the app crashes. I believe this happens because the in-app purchases haven't been fetched from iTunes and clicking the button can't run the purchase process. This crash also happens when the user clicks the button on the first second that the shop screen loads, because – I think – some time is needed to fetch (or request) the products. Here's what I'm talking about:
override func didMove(to view: SKView) {
...
fetchAvailableProducts()
}
func fetchAvailableProducts() {
// Put here your IAP Products ID's
let productIdentifiers = NSSet(objects:
productID100,
productID250,
productID500,
productIDRemoveAds,
productIDUnlockAll)
productsRequest = SKProductsRequest(productIdentifiers: productIdentifiers as! Set<String>)
productsRequest.delegate = self
productsRequest.start()
}
I'm basing my code on this tutorial.
Is there a way to change my code to make it "crash-proof", so that it first checks if the products can be bought to let you use the button?
I use SwiftyStorkeKit ( https://github.com/bizz84/SwiftyStoreKit ) , which uses a completion handler to fill in the products (will save you from reinventing the wheel as well - and is a great resource to learn from)
As for checking network connection, I use Apple's Reachability ( https://developer.apple.com/library/content/samplecode/Reachability/Introduction/Intro.html ). Here is the relevant parts of an implementation. It also check in situations where the app loses focus. You can also use the network check before any store operation.
class vcon: UIViewController {
#IBOutlet weak var noConnectionView: UIView!
func isNetworkAvailable() -> Bool {
//quick test if network is available
var netTest:Reachability? = Reachability(hostName: "apple.com")!
if netTest?.currentReachabilityStatus == .notReachable {
netTest = nil
return false
}
netTest = nil
return true
}
func displayNoNetworkView() {
//this example pulls from a storyboard to a view I have in front of everything else at all times, and shows the view to block everything else if the network isnt available
let ncvc = UIStoryboard(name: "HelpPrefsInfo", bundle: nil).instantiateViewController(withIdentifier: "noNetworkVC") as! noNetworkVC
ncvc.view.frame = noConnectionView.bounds
ncvc.view.backgroundColor = color03
ncvc.no_connection_imageView.tintColor = color01
ncvc.noInternetConnection_label.textColor = color01
noConnectionView.addSubview(ncvc.view)
}
func hideDataIfNoConnection() {
//the actual code that displays the no net connection view
if !isNetworkAvailable() {
if noConnectionView.isHidden == true {
noConnectionView.alpha = 0
noConnectionView.isHidden = false
self.iapObjects = []
UIView.animate(withDuration: 0.50, animations: {
self.noConnectionView.alpha = 1
}, completion:{(finished : Bool) in
});
}
} else {
if noConnectionView.isHidden == false {
self.collection_view.reloadData()
UIView.animate(withDuration: 0.50, animations: {
self.noConnectionView.alpha = 0
}, completion:{(finished : Bool) in
self.noConnectionView.isHidden = true
self.loadIAPData()
});
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(willEnterForeground), name: .UIApplicationDidBecomeActive, object: nil)
displayNoNetworkView()
loadIAPData()
}
func loadIAPData() {
load the data if the network is available
if isNetworkAvailable() {
helper.requestProductsWithCompletionHandler(completionHandler: { (success, products) -> Void in
if success {
self.iapObjects = products!
self.collection_view.reloadData()
} else {
let alert = UIAlertController(title: "Error", message: "Cannot retrieve products list right now.", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
self.present(alert, animated: true, completion: nil)
}
})
}
}
func willEnterForeground() {
hideDataIfNoConnection()
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
hideDataIfNoConnection()
}
I send an async request and I present a view controller when I receive success. It works.
But I'm getting an issue when my app is in background when I receive success and that I pass it in foreground. The view controller is not always displayed.
I think it's about the main thread but I'm not sure.
How can I fix it ?
EDIT:
Here is the function that I call after the success:
func showPopup(on viewController: UIViewController) {
let viewControllerToPresent = MyPopupViewController(nibName: "Popup", bundle: nil)
let popup = PopupDialog(viewController: viewControllerToPresent, buttonAlignment: .horizontal, transitionStyle: .zoomIn, gestureDismissal: false)
let button = DefaultButton(title: "Ok") {
popup.dismiss(animated: true, completion: nil)
}
popup.addButtons([button])
viewController.present(popup, animated: true, completion: nil)
}
When your application is in the background, i.e. suspended, Apple doesn't allow you to make any changes substantive changes to the user interface. In this case your best approach is probably to save that you want to do something on return and check in your App Delegates applicationDidBecomeActive method.
If you call UI functions from the background the result is unpredictable. Just explicitly present on the main thread. Here is a simplified example:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.doBackgroundStuff()
}
func getThread() -> String {
return Thread.isMainThread ? "UI" : "BG"
}
func doBackgroundStuff() {
// force to background
DispatchQueue.global().async {
print("doBackgroundStuff: this is the \(self.getThread()) thread")
self.showPopup(on: self)
}
}
func showPopup(on viewController: UIViewController) {
print("showPopup OUTER: this is the \(self.getThread()) thread")
// force to foreground
DispatchQueue.main.sync {
print("showPopup INNER: this is the \(self.getThread()) thread")
let popup = PresentingViewController(nibName: "PresentingViewController", bundle: nil)
viewController.present(popup, animated: true, completion: nil)
}
}
}
The UI is shown and the output on the console is:
doBackgroundStuff: this is the BG thread
showPopup OUTER: this is the BG thread
showPopup INNER: this is the UI thread
This question already has answers here:
Stop NSTimer and dismiss view controller (swift)
(5 answers)
Closed 6 years ago.
I have a function called "Check" that checks if an object has been updated, if that's the case, the user is sent to another view controller. However, the NSTimer keeps repeating itself, I want it to stop after the user is sent to the other view controller.
func check(){
let current = PFUser.currentUser()?.objectForKey("username")
let check = PFQuery(className: "Requests")
check.whereKey("username", equalTo: current!)
check.whereKey("requestResponded", equalTo: "True")
check.findObjectsInBackgroundWithBlock({ (objects, error) -> Void in
if error != nil || objects == nil{
print("Request rejected.")
} else {
for object in objects!{
let service = object["service"] as! NSValue
print(service)
if service == 1{
self.performSegueWithIdentifier("detailedRequest", sender: self)
print("detailedRequest")
} else {
self.performSegueWithIdentifier("normalRequest", sender: self)
print("normal")
}
}
print("Successfully retrieved that object.")
}
})
}
self.timer = NSTimer.scheduledTimerWithTimeInterval(10.0, target: self, selector: #selector(self.check), userInfo: nil, repeats: true)
Declare a viewDidDisappear function and inside it invalidate your timer. This function automatically be called each time your viewController has disappeared.
Something like this:
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(true)
self.timer.invalidate()
}
I have the following function working as I expect, in iOS 8:
func showConfirmBox(msg:String, title:String,
firstBtnStr:String,
secondBtnStr:String,
caller:UIViewController) {
let userPopUp = UIAlertController(title:title,
message:msg, preferredStyle:UIAlertControllerStyle.Alert)
userPopUp.addAction(UIAlertAction(title:firstBtnStr, style:UIAlertActionStyle.Default,
handler:{action in}))
userPopUp.addAction(UIAlertAction(title:secondBtnStr, style:UIAlertActionStyle.Default,
handler:{action in}))
caller.presentViewController(userPopUp, animated: true, completion: nil)
}
I would like to make something like the following, in order to pass as arguments the methods to be executed when one or the other of the buttons are going to be touched:
func showConfirmBox(msg:String, title:String,
firstBtnStr:String, firstSelector:Selector,
secondBtnStr:String, secondSelector:Selector,
caller:UIViewController) {
let userPopUp = UIAlertController(title:title,
message:msg, preferredStyle:UIAlertControllerStyle.Alert)
userPopUp.addAction(UIAlertAction(title:firstBtnStr, style:UIAlertActionStyle.Default,
handler:{action in caller.firstSelector()}))
userPopUp.addAction(UIAlertAction(title:secondBtnStr, style:UIAlertActionStyle.Default,
handler:{action in caller.secondSelector()}))
caller.presentViewController(userPopUp, animated: true, completion: nil)
}
Obviously I am not doing the right thing with firstSelector and secondSelector, because what I have tried up to now did not work. I suppose I am not using the right syntax for what I want, but I am sure it is possible to do what I would like to do. Any idea of the way to do it properly?
Oneword answer for your question is Closures
The Default Syntax for closures is () -> ()
Instead of Selector you could directly mention the method definition
func showConfirmBox(msg:String, title:String,
firstBtnStr:String, firstSelector:(sampleParameter: String) -> returntype,
secondBtnStr:String, secondSelector:() -> returntype,
caller:UIViewController) {
//Your Code
}
But using this will create readability problems so i suggest you to use typeAlias
typealias MethodHandler1 = (sampleParameter : String) -> Void
typealias MethodHandler2 = () -> Void
func showConfirmBox(msg:String, title:String,
firstBtnStr:String, firstSelector:MethodHandler1,
secondBtnStr:String, secondSelector:MethodHandler2) {
// After any asynchronous call
// Call any of your closures based on your logic like this
firstSelector("FirstButtonString")
secondSelector()
}
You can call your method like this
func anyMethod() {
//Some other logic
showConfirmBox(msg: "msg", title: "title", firstBtnStr: "btnString",
firstSelector: { (firstSelectorString) in
print(firstSelectorString) //this prints FirstButtonString
},
secondBtnStr: "btnstring") {
//Invocation comes here after secondSelector is called
}
}
Just in case anyone else stumbles upon this. I worked out an updated simple solution for Swift 5.1 while I was working through this for while building a global alert utility for a project.
Swift 5.1
Function with Closure:
func showSheetAlertWithOneAction(messageText: String, dismissButtonText: String, actionButtonText : String, presentingView : NSWindow, actionButtonClosure: #escaping () -> Void) {
let alert = NSAlert()
alert.messageText = messageText
alert.addButton(withTitle: actionButtonText)
alert.addButton(withTitle: dismissButtonText)
alert.beginSheetModal(for: presentingView) { (response) in
if response == .alertFirstButtonReturn {
actionButtonClosure()
}
}
}
Function Called:
showSheetAlertWithOneAction(messageText: "Here's a message", dismissButtonText: "Nope", actionButtonText: "Okay", presentingView: self.view.window!) {
someFunction()
}
Adding to got2jam's answer...
If you're working with UIAlertController
The generic function to show an alert with closure:
func showAlertAction(title: String, message: String, actionClosure: #escaping () -> Void){
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "Ok", style: UIAlertAction.Style.default, handler: {(action: UIAlertAction!) in actionClosure()}))
self.present(alertController, animated: true, completion: nil)
}
Now you can call it like that:
showAlertAction(title: "This is the title", message: "This is the message") {
self.close()
}
in this case, close is the particular UIAlertAction to execute
func close(){
dismiss(animated: true, completion: nil)
}
I wrote this routine based on various site examples. Here is how I call the routine...
#IBAction func buttonClick(_ sender: Any) {
SS_Alert.createAlert(parmTitle: "Choose", parmMessage: "Please select Yes or No", parmOptions: ["Yes","No","Cancel"], parmFunctions: [testYes, testNo, nil])
}
func testYes() {
print("yes")
}
func testNo() {
print("no")
}
You can pass in button options and the functions to performed when the buttons are selected. Took a little time to figure out how to pass functions as parameters, but appears to work fine now. I did encounter a strange problem trying to use loops to dynamically add buttons, and finally gave up and used a switch/case. I included the loop code I tried to use, if someone can figure out what I was doing wrong let me know. Thanks.
https://github.com/blakeguitar/iOS/blob/0e243d13cb2decd6e1dbe134a8a046c2caed3876/SS_Alert.swift