How to edit accessory view of keyboard shown from WKWebView? - ios

I am using a WKWebView in my Swift app to present some textfields.
I set some appearance properties to match a specific design, in this case its background has to be blue.
But when the keyboard is triggered by the WKWebView, it does something with the appearance properties and shows the keyboard toolbar in a pale light appearance of my color, do you know why?
The only appearance manipulation on UIToolBar that somewhat worked is this one:
UIToolbar.appearance().backgroundColor = .blue
This is my problem:
This is my goal:

Found a way, ended up to swizzle UIToolbars. Hopefully everything is there, but you would get an idea. Swift 4:
class YourController: UIViewController {
#IBOutlet weak var webView: PWebView!
var toolbar : UIToolbar?
func viewDidLoad() {
webView.addInputAccessoryView(toolbar: self.getToolbar(height: 44))
}
func getToolbar(height: Int) -> UIToolbar? {
let toolBar = UIToolbar()
toolBar.frame = CGRect(x: 0, y: 50, width: 320, height: height)
toolBar.barStyle = .black
toolBar.tintColor = .white
toolBar.barTintColor = UIColor.blue
let doneButton = UIBarButtonItem(title: "Done", style: .plain, target: self, action: #selector(onToolbarDoneClick(sender:)) )
let flexibleSpaceItem = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: self, action: nil )
toolBar.setItems([flexibleSpaceItem, doneButton], animated: false)
toolBar.isUserInteractionEnabled = true
toolBar.sizeToFit()
return toolBar
}
#objc func onToolbarDoneClick(sender: UIBarButtonItem) {
webView?.resignFirstResponder()
}
}
var ToolbarHandle: UInt8 = 0
extension WKWebView {
func addInputAccessoryView(toolbar: UIView?) {
guard let toolbar = toolbar else {return}
objc_setAssociatedObject(self, &ToolbarHandle, toolbar, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
var candidateView: UIView? = nil
for view in self.scrollView.subviews {
let description : String = String(describing: type(of: view))
if description.hasPrefix("WKContent") {
candidateView = view
break
}
}
guard let targetView = candidateView else {return}
let newClass: AnyClass? = classWithCustomAccessoryView(targetView: targetView)
guard let targetNewClass = newClass else {return}
object_setClass(targetView, targetNewClass)
}
func classWithCustomAccessoryView(targetView: UIView) -> AnyClass? {
guard let _ = targetView.superclass else {return nil}
let customInputAccesoryViewClassName = "_CustomInputAccessoryView"
var newClass: AnyClass? = NSClassFromString(customInputAccesoryViewClassName)
if newClass == nil {
newClass = objc_allocateClassPair(object_getClass(targetView), customInputAccesoryViewClassName, 0)
} else {
return newClass
}
let newMethod = class_getInstanceMethod(WKWebView.self, #selector(WKWebView.getCustomInputAccessoryView))
class_addMethod(newClass.self, #selector(getter: WKWebView.inputAccessoryView), method_getImplementation(newMethod!), method_getTypeEncoding(newMethod!))
objc_registerClassPair(newClass!)
return newClass
}
#objc func getCustomInputAccessoryView() -> UIView? {
var superWebView: UIView? = self
while (superWebView != nil) && !(superWebView is WKWebView) {
superWebView = superWebView?.superview
}
guard let webView = superWebView else {return nil}
let customInputAccessory = objc_getAssociatedObject(webView, &ToolbarHandle)
return customInputAccessory as? UIView
}
}

private var keyBordView: UIView?
override func viewDidLoad() {
super.viewDidLoad()
let webView = WKWebView.init(frame: view.bounds)
webView.loadHTMLString("<html><body><div contenteditable='true'></div></body></html>", baseURL: nil)
view.addSubview(webView)
for subview in webView.scrollView.subviews {
if subview.classForCoder.description() == "WKContentView" {
keyBordView = subview
}
}
NotificationCenter.default.addObserver(self, selector: #selector(keyboardShow), name: .UIKeyboardDidShow, object: nil)
// Do any additional setup after loading the view.
}
#objc private func keyboardShow() {
let keyboardToolbar = UIToolbar()
keyboardToolbar.backgroundColor = UIColor.blue
keyboardToolbar.sizeToFit()
let flexBarButton = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
let doneBarButton = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(self.dismissKeyBord))
keyboardToolbar.items = [flexBarButton, doneBarButton]
keyBordView = keyboardToolbar
}

Related

How to implement the 'datepicker' correctly using swift when adding the multiple entries to server?

**Aim: ** To show latest apple calendar type of date picker(inline) in app when user wants to select the date.How it should look
Scenario:**
The user can input multiple entries (maximum 3) the exercise(suryanamaskar). These multiple entries has two functions
1.date-picker and
2.number entry(number of 'suryanamaskar')
When user clicks on the date-picker button. the date picker shows up (.inline). Each entry fetches the post API call that saves the 'suryanamaskar' count entry on server.
What is the issue: I tried to implement the date-picker using and few UI frames : .inline
I replaced the date-picker completely and implemented ".wheels" that works fine.
The datepicker is show up correctly first time. But when the user tried to add other entry or press the date entry buttton . The datepicker gets cut and is not properly visible. This is how it looks when user clicks on datepicker button second time.
What my code looks like:
import UIKit
import SwiftyJSON
import Alamofire
struct surynamaskarStruct {
var date: String
var count: String
}
class AddSuryaNamskarCountVC: UIViewController, UITextFieldDelegate {
var surynamaskarData: [surynamaskarStruct] = [
surynamaskarStruct(date: "Date", count: "0"),
surynamaskarStruct(date: "Date", count: "0"),
surynamaskarStruct(date: "Date", count: "0")
]
#IBOutlet var tableView: UITableView!
#IBOutlet weak var lblMember: UILabel!
#IBOutlet weak var btnMember: UIButton!
#IBOutlet weak var familyMemberHeightConstraint: NSLayoutConstraint!
var viewPicker : UIView!
let datePicker = UIDatePicker()
var btnIndex : Int!
var strMemberId : String!
var dicMember = [[String:Any]]()
lazy var loader : UIActivityIndicatorView = {
let indicator = UIActivityIndicatorView(style: .large)
indicator.hidesWhenStopped = true
return indicator
}()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
if self.dicMember.count > 0 {
self.strMemberId = self.dicMember[0]["id"] as? String
self.lblMember.text = self.dicMember[0]["name"] as? String
self.familyMemberHeightConstraint.constant = 90
} else {
self.familyMemberHeightConstraint.constant = 0
}
view.addSubview(loader)
loader.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
loader.centerXAnchor.constraint(equalTo: view.centerXAnchor),
loader.centerYAnchor.constraint(equalTo: view.centerYAnchor)
])
}
override func viewWillAppear(_ animated: Bool) {
navigationBarDesign(txt_title: "Record Suryanamskar", showbtn: "back")
}
#IBAction func onMemberClick(_ sender: UIButton) {
let vc = self.storyboard?.instantiateViewController(withIdentifier: "PickerTableViewWithSearchViewController") as! PickerTableViewWithSearchViewController
vc.selectedItemCompletion = {dict in
self.lblMember.text = dict["name"] as? String
self.strMemberId = dict["id"] as? String
}
vc.dataSource = dicMember
vc.strNavigationTitle = "Family Member"
vc.modalPresentationStyle = UIModalPresentationStyle.fullScreen
self.present(vc, animated: true, completion: nil)
}
#IBAction func onCancelClick(_ sender: UIButton) {
self.dismiss(animated: true)
}
#IBAction func onSaveClick(_ sender: UIButton) {
var arrData = [[String:Any]]()
for arr in surynamaskarData {
if arr.date != "Date" && arr.count != "0" {
var dict = [String : Any]()
dict["date"] = arr.date
dict["count"] = arr.count
arrData.append(dict)
}
}
if arrData.count > 0 {
self.saveSuryaNamskarCountDataAPI(arrData)
////static controller to directly update the suryanamaskar chart instead of going back and then getting updated suryanamaskar
let alertController = UIAlertController(title:APP.title, message: "Suryanamaskar has been saved successfully!", preferredStyle:.alert)
let Action = UIAlertAction.init(title: "Ok", style: .default) { (UIAlertAction) in
// Write Your code Here
let vc = self.storyboard?.instantiateViewController(withIdentifier: "SuryaNamskarVC") as! SuryaNamskarVC
vc.modalPresentationStyle = UIModalPresentationStyle.fullScreen
// vc.modalTransitionStyle = UIModalTransitionStyle.crossDissolve
self.present(vc, animated: true, completion: nil)
}
alertController.addAction(Action)
self.present(alertController, animated: true, completion: nil)
//// Till here for alert controller. if not implemented, updated suryanamaskar will happen only once user goes back and comes back to suryanamaskarVC
}
// else {
// showAlert(title: APP.title, message: "Please select date and count before Save")
// }
}
// MARK: - DatePicker functions
func showDatePicker(){
viewPicker = UIView(frame: CGRect(x: 0.0, y: self.view.frame.height - 380, width: self.view.frame.width, height: 380))
viewPicker.backgroundColor = UIColor.white
viewPicker.clipsToBounds = true
// Posiiton date picket within a view
datePicker.frame = CGRect(x: 10, y: 50, width: self.view.frame.width, height: 200)
datePicker.datePickerMode = .date
datePicker.minimumDate = Calendar.current.date(byAdding: .month, value: -2, to: Date())
datePicker.maximumDate = Calendar.current.date(byAdding: .year, value: 0, to: Date())
datePicker.backgroundColor = UIColor.white
if #available(iOS 14.0, *) {
datePicker.preferredDatePickerStyle = .inline
} else {
if #available(iOS 13.4, *) {
datePicker.preferredDatePickerStyle = .wheels
} else {
// Fallback on earlier versions
}
// Fallback on earlier versions
}
// Add an event to call onDidChangeDate function when value is changed.
datePicker.addTarget(self, action: #selector(AddMemberStep1VC.datePickerValueChanged(_:)), for: .valueChanged)
datePicker.center.x = self.view.center.x
//ToolBar
var toolbar = UIToolbar();
toolbar.sizeToFit()
toolbar = UIToolbar(frame: CGRect(x: 0.0, y: 0.0, width: self.view.frame.width, height: 50))
let doneButton = UIBarButtonItem(title: "done".localized, style: .plain, target: self, action: #selector(donedatePicker));
let spaceButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.flexibleSpace, target: nil, action: nil)
let cancelButton = UIBarButtonItem(title: "cancel".localized, style: .plain, target: self, action: #selector(cancelDatePicker));
toolbar.setItems([doneButton,spaceButton,cancelButton], animated: false)
// txtDate.inputAccessoryView = toolbar
// txtDate.inputView = datePicker
self.viewPicker.addSubview(toolbar)
self.viewPicker.addSubview(datePicker)
self.viewPicker.clipsToBounds = true
self.view.addSubview(self.viewPicker)
}
#objc func datePickerValueChanged(_ sender: UIDatePicker){
// Create date formatter
let dateFormatter: DateFormatter = DateFormatter()
// Set date format
dateFormatter.dateFormat = "dd/MM/yyyy"
// Apply date format
let _: String = dateFormatter.string(from: sender.date)
// print("Selected value \(selectedDate)")
}
#objc func donedatePicker(){
let formatter = DateFormatter()
formatter.dateFormat = "dd/MM/yyyy"
let strDate = formatter.string(from: datePicker.date)
surynamaskarData[btnIndex].date = strDate
DispatchQueue.main.async {
self.tableView.reloadData()
}
self.viewPicker.isHidden = true
}
#objc func cancelDatePicker(){
self.viewPicker.isHidden = true
}
#objc func onAddMoreClicked() {
print("AddMore Button Pressed")
for i in surynamaskarData {
if i.date == "Date" || i.count == "0" || i.count == "" {
showAlert(title: APP.title, message: "Please select date and count before AddMore")
return
}
}
surynamaskarData.append(surynamaskarStruct(date: "Date", count: "0"))
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
#objc func onDateClick(_ sender: UIButton) {
btnIndex = sender.tag
view.endEditing(true)
if self.viewPicker == nil {
self.showDatePicker()
} else {
if self.viewPicker.isHidden == true {
self.showDatePicker()
} else {
self.viewPicker.isHidden = true
}
}
}
#objc func onDeleteClick(_ sender: UIButton) {
surynamaskarData.remove(at: sender.tag)
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
func textFieldDidBeginEditing(_ textField: UITextField) {
if self.viewPicker != nil {
if self.viewPicker.isHidden == false {
self.viewPicker.isHidden = false
}
}
}
func textFieldDidEndEditing(_ textField: UITextField) {
if textField.text == "" {
textField.text = "0"
}
surynamaskarData[textField.tag].count = textField.text ?? "0"
}
func json(from object:Any) -> String? {
guard let data = try? JSONSerialization.data(withJSONObject: object, options: []) else {
return nil
}
return String(data: data, encoding: String.Encoding.utf8)
}
func saveSuryaNamskarCountDataAPI(_ data : [[String:Any]]) {
var parameters: [String: Any] = [:]
parameters["surynamaskar"] = self.json(from: data)
parameters["member_id"] = self.strMemberId // _appDelegator.dicDataProfile![0]["member_id"] as? String
print(parameters)
// APIUrl.save_suryanamaskar
//live API only
//"https://myhss.org.uk/api/v1/suryanamaskar/save_suryanamaskar_count"
loader.startAnimating()
let url = URL(string: APIUrl.save_suryanamaskar)!
AF.request(url, method: .post, parameters: parameters, encoding: JSONEncoding.default)
.validate()
.responseJSON { response in
self.loader.stopAnimating()
switch response.result {
case .success(let response):
print(response)
let jsonData = JSON(response)
if let status = jsonData["status"].int
{
if status == 1
{
let strMessage : String = jsonData["message"].rawValue as! String
print(strMessage)
// create the alert
let alert = UIAlertController(title: APP.title, message: strMessage, preferredStyle: UIAlertController.Style.alert)
// add an action (button)
let ok = UIAlertAction(title: "Ok".localized, style: .default, handler: { action in
DispatchQueue.main.async {
self.dismiss(animated: true)
}
})
alert.addAction(ok)
// show the alert
self.present(alert, animated: true, completion: nil)
} else {
if let strError = jsonData["message"].string {
showAlert(title: APP.title, message: strError)
}
}
}
case .failure(let error):
print(error.localizedDescription)
showAlert(title: APP.title, message: error.localizedDescription)
}
}
}
}
extension AddSuryaNamskarCountVC : UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return surynamaskarData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: AddSuryaNamskarCountTVCell.cellIdentifier) as! AddSuryaNamskarCountTVCell
cell.btnDate.tag = indexPath.row
cell.btnDate.addTarget(self, action: #selector(self.onDateClick(_:)), for: .touchUpInside)
cell.btnCancel.tag = indexPath.row
cell.btnCancel.addTarget(self, action: #selector(self.onDeleteClick(_:)), for: .touchUpInside)
cell.lblDate.text = surynamaskarData[indexPath.row].date
cell.txtCount.text = surynamaskarData[indexPath.row].count
cell.txtCount.tag = indexPath.row
cell.txtCount.delegate = self
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 60.0
}
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
let viewFooter = UIView()
viewFooter.backgroundColor = UIColor.systemGray5
let size = tableView.frame.size
let addMoreButton = UIButton()
addMoreButton.setTitle("+ Add More", for: .normal)
addMoreButton.setTitleColor(Colors.txtAppDarkColor, for: .normal)
addMoreButton.frame = CGRect(x: 0, y: 0, width: size.width, height: 50)
addMoreButton.addTarget(self, action: #selector(onAddMoreClicked), for: .touchUpInside)
viewFooter.addSubview(addMoreButton)
return viewFooter
}
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 50
}
}
Changing the Co-ordinates works:
Following co-orinates fixes the calender view for this issue.
func showDatePicker(){
viewPicker = UIView(frame: CGRect(x: 0.0, y: self.view.frame.height - 500, width: self.view.frame.width, height: 500))
viewPicker.backgroundColor = UIColor.white
viewPicker.clipsToBounds = true
// Posiiton date picket within a view
datePicker.frame = CGRect(x: 10, y: 50, width: self.view.frame.width, height: 500)
datePicker.datePickerMode = .date
datePicker.minimumDate = Calendar.current.date(byAdding: .month, value: -2, to: Date())
datePicker.maximumDate = Calendar.current.date(byAdding: .year, value: 0, to: Date())
datePicker.backgroundColor = UIColor.white
if #available(iOS 14.0, *) {
datePicker.preferredDatePickerStyle = .inline
} else {
if #available(iOS 13.4, *) {
datePicker.preferredDatePickerStyle = .wheels
} else {
// Fallback on earlier versions
}
// Fallback on earlier versions
}
// Add an event to call onDidChangeDate function when value is changed.
datePicker.addTarget(self, action: #selector(AddMemberStep1VC.datePickerValueChanged(_:)), for: .valueChanged)
datePicker.center.x = self.view.center.x
//ToolBar
var toolbar = UIToolbar();
toolbar.sizeToFit()
toolbar = UIToolbar(frame: CGRect(x: 0.0, y: 0.0, width: self.view.frame.width, height: 50))
let doneButton = UIBarButtonItem(title: "done".localized, style: .plain, target: self, action: #selector(donedatePicker));
let spaceButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.flexibleSpace, target: nil, action: nil)
let cancelButton = UIBarButtonItem(title: "cancel".localized, style: .plain, target: self, action: #selector(cancelDatePicker));
toolbar.setItems([doneButton,spaceButton,cancelButton], animated: false)
self.viewPicker.addSubview(toolbar)
self.viewPicker.addSubview(datePicker)
self.viewPicker.clipsToBounds = true
self.view.addSubview(self.viewPicker)
}

Override var canBecomeFirstResponder not called in a nested collectionViewController

I am struggling to get inputAccessoryView to show up in my UICollectionViewController. Ok here is a simple explanation of what I have.
A UIPageViewController with 3 ViewControllers as pages - so that I can scroll horizontally between them
PageViewController also has segmented view embedded in navigation bar. Have programmed it in a way where when I press a segment, the PageViewController scrolls to the relevant Viewcontroller
One of the ViewControllers in the PageViewController is my ChatViewController
ChatViewController is a UICollectionViewController
Now ignoring this PageViewController, if I simply present the ChatViewController modally, the following code gets called and everything works as expected. I can see the keyboard, type into the input accessory textview and dismiss it.
Code present in ChatViewController
override var inputAccessoryView: UIView? { //IN ChatViewController
get {
if self.typeReply == true{
return viewForReplyInputAccessory
} else {
return viewForInputAccessory
}
}
}
override var canBecomeFirstResponder: Bool {
return true
}
lazy var viewForInputAccessory: KeyboardView = {
let civ = KeyboardView(frame: .init(x: 0, y: 0, width: view.frame.width, height: 50))
civ.sendButton.isUserInteractionEnabled = true
let gcSend = UITapGestureRecognizer(target: self, action: #selector(handleSend))
civ.addGestureRecognizer(gcSend)
return civ
}()
lazy var viewForReplyInputAccessory: KeyboardReplyView = {
let civ = KeyboardReplyView(frame: .init(x: 0, y: 0, width: view.frame.width, height: 114))
civ.sendButton.isUserInteractionEnabled = true
let gcSend = UITapGestureRecognizer(target: self, action: #selector(handleSend))
civ.addGestureRecognizer(gcSend)
let gestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleReplyMessageClose))
civ.closeImageView.addGestureRecognizer(gestureRecognizer)
return civ
}()
The Problem
When this ChatViewController comes nested in this UIPageViewController, for some reason, inputAccessoryView is not shown. override var canBecomeFirstResponder: Bool is not called. Again it works if I simply present the ChatViewController modally without nesting it anywhere. Am I doing something wrong?
For more clarity. Here is my Hierarchy:
UINavigationController (has embedded segmented view) -> UIPageViewController -> [VC1, ChatVC, VC3]
Here is the code for my UIPageViewController
class JobsContainerScreenController: UIPageViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate {
var controllers = [UIViewController]()
var segmentedControl: UISegmentedControl!
var currentSegment: Int = 0
let jobsDetailScreenController = JobsDetailScreenController()
let jobsChatScreenController = ChatController(collectionViewLayout: UICollectionViewFlowLayout())
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
let index = controllers.firstIndex(where: {$0 == viewController}) ?? 0
if index != 1 {
self.currentSegment = index
}
if index == 0{
self.segmentedControl.selectedSegmentIndex = index
return nil
}
self.segmentedControl.selectedSegmentIndex = index
return controllers[index - 1]
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
let index = controllers.firstIndex(where: {$0 == viewController}) ?? 0
if index != 1 {
self.currentSegment = index
}
if index == controllers.count - 1{
self.segmentedControl.selectedSegmentIndex = index
return nil
}
self.segmentedControl.selectedSegmentIndex = index
return controllers[index + 1]
}
override func viewDidLoad() {
super.viewDidLoad()
setupViews()
handleSegment()
}
fileprivate func setupViews() {
view.backgroundColor = .white
self.overrideUserInterfaceStyle = .light
dataSource = self
delegate = self
view.isUserInteractionEnabled = true
let navBarAppearance = UINavigationBarAppearance()
navBarAppearance.configureWithOpaqueBackground()
navBarAppearance.backgroundColor = .white
navBarAppearance.shadowColor = .clear
let backImage = UIImage(systemName: ImageBackArrow)?.withRenderingMode(.alwaysTemplate)
let leftBarButtonItem = UIBarButtonItem(image: backImage, style: .plain, target: self, action: #selector(handleBack))
navigationItem.leftBarButtonItem = leftBarButtonItem
navigationItem.leftBarButtonItem?.tintColor = ColorBlackAlpha
navigationController?.navigationBar.standardAppearance = navBarAppearance
navigationController?.navigationBar.scrollEdgeAppearance = navBarAppearance
self.segmentedControl = UISegmentedControl(items: ["Activity", "Chat", "Contract"])
self.segmentedControl.sizeToFit()
self.segmentedControl.backgroundColor = UIColor.black.withAlphaComponent(0.01)
self.segmentedControl.selectedSegmentTintColor = UIColor.white
self.segmentedControl.selectedSegmentIndex = 0
self.segmentedControl.setTitleTextAttributes([NSAttributedString.Key.font : UIFont(name: FontPromptBold, size: 14)!, NSAttributedString.Key.foregroundColor: ColorBlackLow], for: .normal)
self.segmentedControl.setTitleTextAttributes([NSAttributedString.Key.font : UIFont(name: FontPromptBold, size: 14)!, NSAttributedString.Key.foregroundColor: ColorDarkGreen], for: .selected)
self.segmentedControl.addTarget(self, action: #selector(handleSegment), for: .valueChanged)
self.navigationItem.titleView = segmentedControl
jobsDetailScreenController.job = self.job
jobsChatScreenController.job = self.job
let jobsContractScreenController = JobsContractScreenController()
controllers = [jobsDetailScreenController, jobsChatScreenController, jobsContractScreenController]
setViewControllers([controllers.first!], direction: .forward, animated: false, completion: nil)
}
#objc func handleSegment() {
if self.segmentedControl.selectedSegmentIndex == 0 {
currentSegment = 0
setViewControllers([controllers.first!], direction: .reverse, animated: true, completion: nil)
} else if segmentedControl.selectedSegmentIndex == 1 {
if currentSegment == 0 {
setViewControllers([controllers[1]], direction: .forward, animated: true, completion: nil)
} else {
setViewControllers([controllers[1]], direction: .reverse, animated: true, completion: nil)
}
} else {
currentSegment = 2
setViewControllers([controllers[2]], direction: .forward, animated: true, completion: nil)
}
}
}

After Readed Barcode is not Opened ViewController Swift 3.0

I've created one storyboard with name Main1.storyboard and this storyboard have backend file with ViewController.swift name. All projects im created with Swift 3.0 . This storyboard is for reading barcode, i want to make after reading barcode to open homeViewController.xib - homeViewController.swift file and to send this barcode result. HomeViewController class have Webview components. For me is important just to open homeViewController.xib - homeViewController.swift file after reading barcode and send barcode result. My homeViewController.swift and ViewController.swift class is as given bellow:
homeViewController.swift:
import UIKit
import SystemConfiguration
import DrawerController
import SVProgressHUD
class homeViewController: UIViewController , UIWebViewDelegate,UIScrollViewDelegate {
var webView: UIWebView!
var text:String = "http://www.google.com"
var barcode:String = ""
override func viewDidLoad() {
super.viewDidLoad()
// setup title
self.title = "My App"
// setup NavigationBar Color
self.navigationController?.navigationBar.barTintColor = navigationBarColor
let ud: UserDefaults = UserDefaults.standard
let data: NSData? = ud.object(forKey: "cookie") as? NSData
if let cookie = data {
let datas: NSArray? = NSKeyedUnarchiver.unarchiveObject(with: cookie as Data) as? NSArray
if let cookies = datas {
for c in cookies as! [HTTPCookie] {
HTTPCookieStorage.shared.setCookie(c)
}
}
}
// setup side Bar
if(sideBarPosition == "left"){
self.setupLeftMenuButton()
}
else{
self.setupRightMenuButton()
}
// setup WebView
webView = UIWebView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height-64))
self.view.addSubview(webView)
webView.delegate = self
webView.scrollView.delegate = self
webView.isUserInteractionEnabled = false
SVProgressHUD.show()
if (typeOfData == "html"){
// load from html file
let myfile = Bundle.main.url(forResource: nameFileHtml, withExtension: "html")
let requestObj = NSURLRequest(url: myfile!)
webView.loadRequest(requestObj as URLRequest)
}
else{
// load website data
if(connectedToNetwork() == false){
SVProgressHUD.showError(withStatus: "No Internet Connection")
}
else{
webView.loadRequest(URLRequest(url: URL(string: text)!))
}
}
}
// setup left menu button icon
func setupLeftMenuButton() {
let button = UIButton.init(type: .custom)
button.setImage(UIImage.init(named: "menuleft.png")?.withRenderingMode(.alwaysTemplate), for: UIControlState.normal)
button.addTarget(self, action:#selector(leftDrawerButtonPress), for: UIControlEvents.touchUpInside)
button.frame = CGRect.init(x: 0, y: 0, width: 30, height: 17) //CGRectMake(0, 0, 30, 30)
button.imageView?.tintColor = iconColor
let barButton = UIBarButtonItem.init(customView: button)
self.navigationItem.leftBarButtonItem = barButton
}
// setup left menu button action
func leftDrawerButtonPress(_ sender: AnyObject?) {
self.evo_drawerController?.toggleDrawerSide(.left, animated: true, completion: nil)
}
// setup right menu button icon
func setupRightMenuButton() {
let button = UIButton.init(type: .custom)
button.setImage(UIImage.init(named: "menuright.png")?.withRenderingMode(.alwaysTemplate), for: UIControlState.normal)
button.addTarget(self, action:#selector(rightDrawerButtonPress), for: UIControlEvents.touchUpInside)
button.frame = CGRect.init(x: 0, y: 0, width: 30, height: 17) //CGRectMake(0, 0, 30, 30)
button.imageView?.tintColor = iconColor
let barButton = UIBarButtonItem.init(customView: button)
self.navigationItem.rightBarButtonItem = barButton
}
// setup right menu button action
func rightDrawerButtonPress(_ sender: AnyObject?) {
self.evo_drawerController?.toggleDrawerSide(.right, animated: true, completion: nil)
}
// setup webView option
func webViewDidStartLoad(_ webView: UIWebView)
{
SVProgressHUD.show()
}
func webViewDidFinishLoad(_ webView: UIWebView)
{
if( barcode != "")
{
self.webView.stringByEvaluatingJavaScript(from:
"var element = document.getElementById('input-password');"
+ "element.value ="+barcode+";" +
"")
}
let cookieJar: HTTPCookieStorage = HTTPCookieStorage.shared
let data: NSData = NSKeyedArchiver.archivedData(withRootObject: cookieJar.cookies) as NSData
let ud: UserDefaults = UserDefaults.standard
ud.set(data, forKey: "cookie")
SVProgressHUD.dismiss()
webView.isUserInteractionEnabled = true
let aLabel = UIButton()
self.view.addSubview(aLabel)
aLabel.widthAnchor.constraint(equalToConstant: 100.0).isActive = true
aLabel.heightAnchor.constraint(equalToConstant: 123.0).isActive = true
aLabel.setImage(UIImage(named: "photo-camera.png"), for: .normal)
aLabel.translatesAutoresizingMaskIntoConstraints = false
let horizontalConstraint = NSLayoutConstraint(item: aLabel, attribute: NSLayoutAttribute.trailing, relatedBy: NSLayoutRelation.equal, toItem: view, attribute:NSLayoutAttribute.trailing, multiplier: 1, constant: 0)
let verticalConstraint = NSLayoutConstraint(item: aLabel, attribute: NSLayoutAttribute.bottom, relatedBy: NSLayoutRelation.equal, toItem: view, attribute: NSLayoutAttribute.bottom, multiplier: 1, constant: 0)
aLabel.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
NSLayoutConstraint.activate([horizontalConstraint, verticalConstraint])
self.view.layoutIfNeeded()
}
#objc func buttonAction(sender: UIButton!) {
let storyBoard: UIStoryboard = UIStoryboard(name: "Main1", bundle: nil)
let balanceViewController = storyBoard.instantiateViewController(withIdentifier: "barcode") as! ViewController
self.present(balanceViewController, animated: true, completion: nil)
}
func btnclicked(sender: UIButton!)
{
let alert = UIAlertController(title: "Alert", message: "unwraped.stringValue", preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "Click", style: UIAlertActionStyle.default, handler: nil))
self.present(alert, animated: true, completion: nil)
}
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
if (scrollView.contentOffset.y < 0){
self.webView.reload()
}
}
// check connection
func connectedToNetwork() -> Bool {
var zeroAddress = sockaddr_in()
zeroAddress.sin_len = UInt8(MemoryLayout<sockaddr_in>.size)
zeroAddress.sin_family = sa_family_t(AF_INET)
guard let defaultRouteReachability = withUnsafePointer(to: &zeroAddress, {
$0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
SCNetworkReachabilityCreateWithAddress(nil, $0)
}
}) else {
return false
}
var flags: SCNetworkReachabilityFlags = []
if !SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags) {
return false
}
let isReachable = flags.contains(.reachable)
let needsConnection = flags.contains(.connectionRequired)
return (isReachable && !needsConnection)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
ViewController.swift
import UIKit
import AVFoundation
class ViewController: UIViewController,UIScrollViewDelegate , UIWebViewDelegate , AVCaptureMetadataOutputObjectsDelegate {
#IBOutlet weak var viewPreview: UIView!
var captureSession: AVCaptureSession?
var videoPreviewLayer: AVCaptureVideoPreviewLayer!
var isReading: Bool = false
var barcoderesult:String=""
#IBOutlet weak var anulo_btn: UIButton!
#IBAction func bnt_cancel(_ sender: Any) {
let vc = homeViewController(nibName: "homeViewController", bundle: nil)
let navigationController = UINavigationController(rootViewController: vc)
self.evo_drawerController?.setCenter(navigationController, withCloseAnimation: true, completion: nil)
self.dismiss(animated: true)
}
// setup left menu button icon
func setupLeftMenuButton() {
let button = UIButton.init(type: .custom)
button.setImage(UIImage.init(named: "menuleft.png")?.withRenderingMode(.alwaysTemplate), for: UIControlState.normal)
button.addTarget(self, action:#selector(leftDrawerButtonPress), for: UIControlEvents.touchUpInside)
button.frame = CGRect.init(x: 0, y: 0, width: 30, height: 17) //CGRectMake(0, 0, 30, 30)
button.imageView?.tintColor = iconColor
let barButton = UIBarButtonItem.init(customView: button)
self.navigationItem.leftBarButtonItem = barButton
}
// setup left menu button action
func leftDrawerButtonPress(_ sender: AnyObject?) {
self.evo_drawerController?.toggleDrawerSide(.left, animated: true, completion: nil)
}
// MARK: - View Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.navigationBar.barTintColor = navigationBarColor
// setup title
setupLeftMenuButton()
viewPreview.layer.cornerRadius = 5;
captureSession = nil;
if !isReading {
if (self.startReading()) {
}
}
else {
stopReading()
}
isReading = !isReading
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - IBAction Method
#IBAction func startStopClick(_ sender: UIButton) {
if !isReading {
if (self.startReading()) {
}
}
else {
stopReading()
}
isReading = !isReading
}
// MARK: - Custom Method
func startReading() -> Bool {
let captureDevice = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo)
do {
let input = try AVCaptureDeviceInput(device: captureDevice)
captureSession = AVCaptureSession()
captureSession?.addInput(input)
// Do the rest of your work...
} catch let error as NSError {
// Handle any errors
print(error)
return false
}
videoPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
videoPreviewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
videoPreviewLayer.frame = viewPreview.layer.bounds
viewPreview.layer.addSublayer(videoPreviewLayer)
/* Check for metadata */
let captureMetadataOutput = AVCaptureMetadataOutput()
captureSession?.addOutput(captureMetadataOutput)
captureMetadataOutput.metadataObjectTypes = captureMetadataOutput.availableMetadataObjectTypes
print(captureMetadataOutput.availableMetadataObjectTypes)
captureMetadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
captureSession?.startRunning()
return true
}
func stopReading() {
captureSession?.stopRunning()
captureSession = nil
videoPreviewLayer.removeFromSuperlayer()
}
func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [Any]!, from connection: AVCaptureConnection!) {
for data in metadataObjects {
let metaData = data as! AVMetadataObject
print(metaData.description)
let transformed = videoPreviewLayer?.transformedMetadataObject(for: metaData) as? AVMetadataMachineReadableCodeObject
if let unwraped = transformed {
print(unwraped.stringValue)
// lblString.text = unwraped.stringValue
// btnStartStop.setTitle("Start", for: .normal)
barcoderesult = unwraped.stringValue
let alert = UIAlertController(title: "Alert", message: unwraped.stringValue, preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "Click", style: UIAlertActionStyle.default, handler: nil))
self.present(alert, animated: true, completion: nil)
self.performSelector(onMainThread: #selector(stopReading), with: nil, waitUntilDone: false)
isReading = false;
}
}
}
}
It's difficult to answer your question without providing the error you gets when you run or what kind of error is listed in the console.
BTW, i guess you may haven't set the identifier for the viewController in each storyboards.

Failed that ImageView in scrollView zoom in by swift3

Sorry I am new to swift and I have an imageView.
I can see my image in imageView.
I want to be able to zoom the image in and out. But I also fail to change my image size. What's wrong with my code? Sorry,I already find many solution to fix my problem. I don't know how to fix it. Should I add some gestureRecognizer or anything else?
Thank for answer.
This is code:
import UIKit
import SnapKit
class ImageViewController: UIViewController, UIScrollViewDelegate {
var url:String = ""
var id:String = ""
var scrollView:UIScrollView = { () -> UIScrollView in
let ui = UIScrollView()
ui.backgroundColor = UIColor.black
ui.minimumZoomScale = 1.0
ui.maximumZoomScale = 6.0
return ui
}()
var toolbar:UIToolbar = { () -> UIToolbar in
let ui = UIToolbar()
ui.barTintColor = defaultNavBackgroundColor
ui.clipsToBounds = true
ui.isTranslucent = false
ui.isUserInteractionEnabled = true
return ui
}()
var photoImageView:UIImageView = { () -> UIImageView in
let ui = UIImageView()
ui.backgroundColor = UIColor.clear
ui.layer.masksToBounds = true
ui.contentMode = .scaleAspectFit
ui.isUserInteractionEnabled = true
return ui
}()
var buttonDownload:UIBarButtonItem = { () -> UIBarButtonItem in
let ui = UIBarButtonItem()
ui.style = .plain
return ui
}()
override func loadView() {
super.loadView()
view.backgroundColor = UIColor.black
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
override func viewDidLoad() {
super.viewDidLoad()
// navigationItem.title = name
self.automaticallyAdjustsScrollViewInsets = false
imageFromUrl(imageView: photoImageView, url: url, chatroomId: id,isResize: false)
self.buttonDownload.action = #selector(btnDownloadClicked(sender:))
scrollView.delegate = self
scrollView.contentSize = (self.photoImageView.image!.size)
photoImageView.center = scrollView.center
loadContent()
loadVFL()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func loadContent() {
toolbar.sizeToFit()
let flexSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: self, action: nil)
buttonDownload = UIBarButtonItem(barButtonSystemItem: .action, target: self, action: nil)
toolbar.items = [flexSpace,buttonDownload]
view.addSubview(scrollView)
view.addSubview(toolbar)
scrollView.addSubview(photoImageView)
}
func loadVFL() {
scrollView.snp.makeConstraints { (make) -> Void in
make.top.equalToSuperview()
make.bottom.equalTo(toolbar.snp.top)
make.left.equalToSuperview()
make.right.equalToSuperview()
}
photoImageView.snp.makeConstraints { (make) -> Void in
make.top.equalToSuperview()
make.bottom.equalToSuperview()
make.left.equalToSuperview()
make.right.equalToSuperview()
make.centerX.equalToSuperview()
make.centerY.equalToSuperview()
}
toolbar.snp.makeConstraints { (make) -> Void in
make.width.equalToSuperview()
make.bottom.equalToSuperview()
make.height.equalTo(44)
}
}
func btnDownloadClicked(sender:UIBarButtonItem) {
print("press")
}
// MARK: - UIScrollViewDelegate
func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? {
return photoImageView
}
}
image like this

Adding Done/Next to numberPad

I have read the post on how to create these buttons but they use a tag system to identify the textFields. I am using a prototype cell to create my textFields
func addDoneButtonOnKeyboard(_ hasNextBtn: Bool = false) -> UIView {
let doneToolbar: UIToolbar = UIToolbar(frame: CGRect(x: 0, y: 163, width: 106, height: 53))
doneToolbar.barStyle = UIBarStyle.default
let flexSpace = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.flexibleSpace, target: nil, action: nil)
let done: UIBarButtonItem = UIBarButtonItem(title: "Done",
style: UIBarButtonItemStyle.done,
target: self,
action: #selector(doneAction))
let next: UIBarButtonItem = UIBarButtonItem(title: "Next",
style: .plain,
target: self,
action: #selector(nextAction))
var items = [UIBarButtonItem]()
items.append(flexSpace)
if hasNextBtn {
items.append(next)
} else {
items.append(done)
}
doneToolbar.items = items
doneToolbar.sizeToFit()
return doneToolbar
}
#objc func doneAction() {
self.view.endEditing(true)
}
#objc func nextAction(_ textField: UITextField) -> Bool {
let indexPath = IndexPath(row: index, section: 2)
// if textField.returnKeyType == .next {
if textField.inputAccessoryView == next as! UIView? {
guard let table = tableView, let ip = indexPath,
let cell = table.cellForRow(at: IndexPath(row: ip.row + 1, section: ip.section)) as? FormFieldTableViewCell
else { return true }
return cell.becomeFirstResponder()
}
return true
}
You can change Change button Type on Keyboard as follows,
// Assign in your cellForRowAtIndexPath method
yourTextField.returnKeyType = .Next
txtField.returnKeyType = .Done
txtField.returnKeyType = .Go
And Many more from below,
case Default
case Go
case Google
case Join
case Next
case Route
case Search
case Send
case Yahoo
case Done
case EmergencyCall
You can after handle action as desire in UITextFieldDelegate.

Resources