I'm trying to make a pull to refresh on WKWebView. When I try to pull and refresh, I get this error:'NSInvalidArgumentException', reason: '-[GoldenVillage.AvailabilityViewController mymethodforref:]: unrecognized selector sent to instance 0x111d18e50'
How to fix it?
My code in viewDidAppear :
self.availabilityWebview = WKWebView(
frame: self.containerView.bounds,
configuration: config
)
self.view = self.availabilityWebview!
refController.bounds = CGRectMake(0, 50, refController.bounds.size.width, refController.bounds.size.height)
refController.addTarget(self, action: Selector(("mymethodforref:")), for: UIControlEvents.valueChanged)
refController.attributedTitle = NSAttributedString(string: "Pull to refresh")
availabilityWebview?.scrollView.addSubview(refController)
self.noNetwork.text! = ""
if let crew = user!["crew"] as? [String:Any], let crewID = crew["crew_id"] as? String {
let url = URL(string: "http://ec2-52-221-231-3.ap-southeast-1.compute.amazonaws.com/gv/available-schedule_3.php?id=\(crewID)")
self.availabilityWebview!.load(URLRequest(url:url!))
func mymethodforref(refresh:UIRefreshControl){
availabilityWebview?.reload()
refController.endRefreshing()
}
func webViewDidFinishLoad(_ webView: UIWebView) {
UIApplication.shared.isNetworkActivityIndicatorVisible = false
}
func webViewDidStartLoad(_ webView: UIWebView) {
UIApplication.shared.isNetworkActivityIndicatorVisible = true
}
instead of
Selector(("mymethodforref:")
try
#selector(mymethodforref(refresh:))
Related
How can I change the progress of a progress bar while another tab of the TabBarController is selected?
Code:
self.RAMBar.progress = Float(response.out)/100
Error:
2022-04-14 09:51:33.027911+0200 ServiceTOOLS Control[4195:217038] ServiceTOOLS_Control/ViewController.swift:299: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
Additional info:
I've only got a ViewController.swift that handles all tabs, this is the erroring code:
func parseRAM(){
guard let url = URL(string: "http://\(hostname):\(port)/STOInfo/Stats/RAM")else{
return
}
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
let body: [String: AnyHashable] = [
"username": username,
"password": password,
]
request.httpBody = try? JSONSerialization.data(withJSONObject: body, options: .fragmentsAllowed)
let task = URLSession.shared.dataTask(with: request) {data, _, error in
guard let data=data, error == nil else{
return
}
do{
let response = try JSONDecoder().decode(statsServerInfo.self, from: data)
print("SUCCESS: \(response)")
DispatchQueue.main.async{
if (self.tabBarController?.tabBar.selectedItem?.tag == 0){
self.RAMBar.progress = Float(response.out)/100
}
}
}
catch{
print(error)
}
}
task.resume()
}
#IBOutlet var CPUBar: UIProgressView!
#IBOutlet var RAMBar: UIProgressView!
The UIProgressViews are on the first tab. The function automatically runs every 10 seconds with a timer. The error is thrown here: self.RAMBar.progress = Float(response.out)/100
The error happens only if I switch tabs, then I tried putting an if, to check the tab and now, with this code, it happens when, after I switch tabs, I return on the first tab. The exact output is:
SUCCESS: statsServerInfo(ok: true, out: 4)
ServiceTOOLS_Control/ViewController.swift:307: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
2022-04-14 14:52:59.783278+0200 ServiceTOOLS Control[4837:261227] ServiceTOOLS_Control/ViewController.swift:307: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
Here is an example to use UITabBarController.
I have created two ViewControllers and add a UIProgressView in one UIViewController. Here is the code of ViewController.swift file.
import UIKit
class ViewController: UIViewController {
var timer: Timer!
var progressBarValue: Float = 0
let progressBar: UIProgressView = {
let progressView = UIProgressView(progressViewStyle: .bar)
progressView.translatesAutoresizingMaskIntoConstraints = false
progressView.progress = 0
progressView.trackTintColor = .lightGray
progressView.tintColor = .blue
return progressView
}()
override func viewDidLoad() {
super.viewDidLoad()
print("Main VC")
view.backgroundColor = .white
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateProgressBar), userInfo: nil, repeats: true)
view.addSubview(progressBar)
progressBar.center = view.center
}
#objc func updateProgressBar() {
print("Timer called")
progressBarValue += 0.01
progressBar.progress = progressBarValue
}
}
And this is the code of another ViewController namely SecondViewController.swift
import UIKit
class SecondViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
print("Second VC")
view.backgroundColor = .cyan
}
}
Now you have to add these ViewControllers to UITabBarController. I have create a class to programatically add those ViewController. Here is the code of MainTabBarController.swift file
import UIKit
class MainTabViewController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
let mainVC = ViewController()
mainVC.tabBarItem = UITabBarItem(title: "Tab 1", image: UIImage(systemName: "circle"), selectedImage: UIImage(systemName: "circle.fill"))
let secondVC = SecondViewController()
secondVC.tabBarItem = UITabBarItem(title: "Tab 2", image: UIImage(systemName: "square"), selectedImage: UIImage(systemName: "square.fill"))
viewControllers = [mainVC, secondVC]
}
}
Now i set this MainTabBarController as rootViewController programatically in SceneDelegate. It's absolutely ok if you are using Storyboard. Just Set TabBarController as InitialViewController.
import UIKit
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
let window = UIWindow(windowScene: windowScene)
window.rootViewController = MainTabViewController()
self.window = window
window.makeKeyAndVisible()
}
}
I have tested this code. The UIProgressView in ViewController class is updating even if i move to SecondViewController in MainTabBarController. Hope you get the idea.
I recently updated an app I'm working on to IOS15 and I've hit a snag while testing.
When the user tries to log into the app using FirebaseUI (with google or email) the login process completes but does not trigger the callback function didSignInWith: from the delegate.
None of the print statements are being called from this class, but if I restart the app it behaves as if login was successful
Does anybody know what I've done wrong here?... as far as I can tell everything should work fine
import UIKit
import UserNotifications
import FirebaseUI
class LoginViewCell: UICollectionViewCell, FUIAuthDelegate {
let imageView: UIImageView
let title: UILabel
let text: UILabel
var parent: WelcomeViewController? = nil
var authUI: FUIAuth
override init(frame: CGRect) {
authUI = FUIAuth.defaultAuthUI()!
super.init(frame: frame)
let providers: [FUIAuthProvider] = [FUIGoogleAuth(authUI: authUI), FUIEmailAuth()
]
authUI.providers = providers
authUI.delegate = self
let loginButton = UIButton(frame: CGRect(x: 100, y: 100, width: 300, height: 50))
loginButton.backgroundColor = .blue
loginButton.setTitle("Let's Get Started!", for: .normal)
loginButton.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
loginButton.center = CGPoint(x: frame.width / 2, y: ((frame.height/4) * 3) + 50)
addSubview(loginButton)
}
#objc func buttonAction(sender: UIButton!) {
let authViewController = authUI.authViewController()
parent?.present(authViewController , animated: true, completion: nil)
}
//MARK: Authentication Delegate
func authUI(_ authUI: FUIAuth, didSignInWith user: User?, error: Error?) {
// handle user and error as necessary
print("Login attempted")
if( error != nil){
print("Error Logging In \(String(describing: error))")
}else{
userHandler.doesUserExist({(exists: Bool) in
if(exists){
self.parent?.dismiss(animated: true, completion: nil)
}else{
let manager = UserManagerViewController()
manager.shouldCreateUser = true
let nvc = CalendarNavigationController(rootViewController: manager)
self.parent?.present(nvc, animated: true, completion: nil)
}
})
}
}
func application(_ app: UIApplication, open url: URL,
options: [UIApplication.OpenURLOptionsKey : Any]) -> Bool {
let sourceApplication = options[UIApplication.OpenURLOptionsKey.sourceApplication] as! String?
if FUIAuth.defaultAuthUI()?.handleOpen(url, sourceApplication: sourceApplication) ?? false {
print("Think did log in successful")
return true
}
// other URL handling goes here.
print("think did not log in")
return false
}
}
Is your cell still displayed when the callback should get called ? If not then here's the problem :
Your cell object can be deallocated (managed by the UICollectionView), and it happens that it's also the delegate for firebase. When that happens, firebase call a delegate that is nil, and it does nothing.
What you can do is create a specific class that will conform to the delegate protocol, keep a strong reference to it in a controller or viewModel that won't be terminated unless you want it (here the controller of viewModel is the object that initialize the cell), pass it as a dependency to the cell object (dependency injection), and set it as the authUI.delegate. If you follow these instructions the delegate will be retained cause it has a strong reference.
I have created custom navigation bar with cart and its count like below code: like this if i call setUpNavigationBar() in all viewcontrollers i am getting navigationbar with cart and count
func setUpNavigationBar(){
self.navigationController?.navigationBar.backgroundColor = CommonColor.navigationColor
self.navigationController?.navigationBar.barTintColor = CommonColor.navigationColor
self.navigationController?.navigationBar.isTranslucent = false
self.navigationController?.navigationBar.layer.masksToBounds = false
let cartBtn: BadgeButton = BadgeButton(type: .custom)
cartBtn.setImage(#imageLiteral(resourceName: "icon55"), for: .normal)
cartBtn.addAction(for: .touchUpInside) {
print("in cart")
let signupVC = (StoryBoard.driver).instantiateViewController(identifier: "ShoppingCartVC") as! ShoppingCartVC
self.navigationController?.pushViewController(signupVC, animated: true)
}
cartBtn.frame = CGRect(x: 0, y: 0, width: 20, height: 20)
let cartQty = UserDefaults.standard.value(forKey: "cartCount")
cartBtn.badgeText = cartQty as? String
let btnCart = UIBarButtonItem(customView: cartBtn)
self.navigationItem.setRightBarButton(btnCart, animated: true)
}
func setBadgeCountForCart(with : String?){
let badge = self.navigationItem.rightBarButtonItems?.map({$0.customView}).filter({$0 is BadgeButton}).first as? BadgeButton
badge?.badgeText = with
}
but in ShoppingCartVC i don't want navigation bar so, i didn't call setUpNavigationBar in ShoppingCartVC viewdidload
so if remove product from cart and i click back button in ShoppingCartVC then the cartConut is not updating in poped viewcontroller
after remove product i have updated cartCount like below:
class ShoppingCartVC: UIViewController {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
self.navigationController?.navigationBar.isHidden = true
self.serviceCall()
}
func serviceCall(){
self.cartDB = ShoppingCartDetaModel(dictionary: responseData.dict as NSDictionary? ?? NSDictionary())
UserDefaults.standard.set(self.cartDB?.result?.cart?.total_item, forKey: "cartCount")
self.setUpNavigationBar()
}
#IBAction func back(_ sender: UIButton){
self.navigationController?.navigationBar.isHidden = false
self.serviceCall()
UserDefaults.standard.set(self.cartDB?.result?.cart?.total_item, forKey: "cartCount")
self.setUpNavigationBar()
self.navigationController?.popViewController(animated: true)
}
}
where should i change, please do help
You need to update the BadgeButton its text property with the updated quantity after you navigated back. You can use different methods for achieving that. The easiest and less clean one is to call self.setUpNavigationBar() in the viewWillAppear method. A cleaner way is to use the NotificationCenter class or a Publisher and post an update every time the quantity changes.
ViewWillAppear: https://developer.apple.com/documentation/uikit/uiviewcontroller/1621510-viewwillappear
NotificationCenter: https://developer.apple.com/documentation/foundation/notificationcenter
Publisher: https://developer.apple.com/documentation/combine/publisher
Hi I want to open sms app with teat along url I write following code but I facing
error like:Static member 'canSendText' cannot be used on instance of type 'MFMessageComposeViewController'
var controller1 = MFMessageComposeViewController()
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if indexPath.section == 0
{
if (controller1.canSendText()) {
let urlToShare = "http://www.appzoy.com"
controller1.body = "Hey I just gave an Awesome Assessment on UAssess App you can also try it. I scored , Try to beat my score \(urlToShare)"
controller1.messageComposeDelegate = self as? MFMessageComposeViewControllerDelegate
self.present(controller1, animated: true, completion: nil)
}
}
}
As mentioned into doc you should use:
if MFMessageComposeViewController.canSendText() {
print("SMS services are available")
}
Try This
//MARK:- Send Mail
//MARK:-
func sendMail(emailTitle : String , messageBody : String , toRecipents : [String]){
if (MFMailComposeViewController.canSendMail()) {
let mc: MFMailComposeViewController = MFMailComposeViewController()
let emailTitle = emailTitle //Feedback
let messageBody = messageBody //"Feature request or bug report?"
let toRecipents = toRecipents //["friend#stackoverflow.com"]
mc.mailComposeDelegate = self
mc.setSubject(emailTitle)
mc.setMessageBody(messageBody, isHTML: false)
mc.setToRecipients(toRecipents)
self.present(mc, animated: true, completion: nil)
}else{
//Show alert
}
}
The call sendMail function
let urlToShare = "http://www.appzoy.com"
self.sendMail(emailTitle: "Title of Mail", messageBody: "Hey I just gave an Awesome Assessment on UAssess App you can also try it. I scored , Try to beat my score \(urlToShare)", toRecipents: ["abc#mail.com", "def#mail.com"])
I've switched one of my apps to use WKWebview and it's the first time for me, I had a problem which is as you know I can't control it from the storyboard, I have a scrollview inside a sidebar that comes out when you click the button on the upper left, but it seems like it appears in the back of the view, how can I fix this please?
let configuration = WKWebViewConfiguration()
configuration.preferences = preference
webView = WKWebView(frame: view.bounds, configuration: configuration)
view.addSubview(webView)
please note that I already clicked the button in that screeshot but the scroll didn't show]1
Use this,
first import
import WebKit
set delegate of WKWebview
WKScriptMessageHandler , WKNavigationDelegate
declare webview
var webView: WKWebView!
var webConfiguration:WKWebViewConfiguration! = nil
override func loadView() {
webView = WKWebView(frame: .zero, configuration: self.webConfig())
webView.navigationDelegate = self
view = webView
}
viewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
/*if let url = URL(string: "http://192.168.1.122/arvee/piyush/mobileViewPages/owner/manager.html") {
let request = URLRequest(url: url)
webView.load(request)
}*/
let url = Bundle.main.url(forResource: "AddAppointment/bookApp", withExtension:"html")
let request = URLRequest(url: url!)
webView.load(request)
}
WKWebview Delegate
func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
print(error)
}
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
let data = JSON(message.body)
}
func webConfig() -> WKWebViewConfiguration {
//if webConfiguration != false {
webConfiguration = WKWebViewConfiguration()
let userController = WKUserContentController()
userController.add(self, name: "buttonClicked")
userController.add(self, name: "pageLoaded")
webConfiguration.userContentController = userController
//}
return webConfiguration
}