How to add UIButton action in closure in custom UIToolbar class initialization? - ios

I have a custom UIToolbar class with two UIBarButtonItem. I know I can create custom delegate action for UIBarButtonItem item. But how can I use closure as UIBarButtonItem action in custom UIToolbar class initialization?
class KeyboardToolBar: UIToolbar
{
let done = UIButton.init()
init() {
super.init(frame: .zero)
self.backgroundColor = UIColor.gray
self.sizeToFit()
let flexBarBtn = UIBarButtonItem.init(barButtonSystemItem: UIBarButtonSystemItem.flexibleSpace, target: nil, action: nil)
done.frame = CGRect(x: 0, y: 0, width: 50, height: 44)
done.setTitle("Done", for: .normal)
done.setTitleColor(.black, for: .normal)
let doneBarBtn:UIBarButtonItem! = UIBarButtonItem.init(customView: done)
self.items = [flexBarBtn,doneBarBtn]
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
In ViewController
super.viewDidLoad()
txt.keyboardType = .numberPad
txt.inputAccessoryView = KeyboardToolBar()
// How can I use some thing like this
// txt.inputAccessoryView = KeyboardToolBar(doneBtnAction: {
// print("done button pressed")
// })
}

Here you go ...
class KeyboardToolBar: UIToolbar {
let done = UIButton.init()
var doneBtnAction:((Void) -> Void)?
convenience init(_ doneBtnAction: #escaping (Void) -> Void) {
self.init()
self.doneBtnAction = doneBtnAction
}
private init() {
super.init(frame: .zero)
self.backgroundColor = UIColor.gray
self.sizeToFit()
let flexBarBtn = UIBarButtonItem.init(barButtonSystemItem: UIBarButtonSystemItem.flexibleSpace, target: nil, action: nil)
done.frame = CGRect(x: 0, y: 0, width: 50, height: 44)
done.setTitle("Done", for: .normal)
done.setTitleColor(.black, for: .normal)
done.addTarget(self, action: #selector(callbackDoneButton(_:)), for: .touchUpInside)
let doneBarBtn:UIBarButtonItem! = UIBarButtonItem.init(customView: done)
self.items = [flexBarBtn,doneBarBtn]
}
func callbackDoneButton(_ id:Any) -> Void {
if self.doneBtnAction != nil {
self.doneBtnAction!()
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
In ViewController
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
txt.keyboardType = .numberPad
txt.inputAccessoryView = KeyboardToolBar.init( {
(Void) -> Void in
print("done");
})
}

Related

Swift - Refresh Custom UITabBarController Bar

I have a custom TabBar, with a raised middle button, above the TabBar. When the user has not completed a daily task, the setupIncompleteMiddleButton() should appear, indicating that. However, once the user completes the task, I would like setupCompleteMiddleButton() to appear, indicating the have completed the task. I don't know how to do this - I shouldn't call viewDidLoad() in the controller and when calling it, it does nothing to refresh the view. Refreshing the TabBar does nothing.
This is my TabBar controller:
class TabBarController: UITabBarController, UITabBarControllerDelegate {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
}
override func viewDidLoad() {
super.viewDidLoad()
// This is where I currently decide which button to show the "complete" one if the task is done, and the incomplete one if not.
self.delegate = self
if UserData.hasCompletedDailyTask() == true {
setupCompleteMiddleButton()
} else {
setupIncompleteMiddleButton()
}
}
// Incomplete button
func setupIncompleteMiddleButton() {
let middleButton = UIButton(frame: CGRect(x: (self.view.bounds.width / 2) - 25, y: -20, width: 50, height: 50))
middleButton.backgroundColor = UIColor.systemYellow
middleButton.setImage(UIImage(systemName: "sun.max.fill"), for: .normal)
middleButton.imageView?.tintColor = UIColor.white
middleButton.layer.cornerRadius = middleButton.frame.width / 2
middleButton.clipsToBounds = true
self.tabBar.addSubview(middleButton)
middleButton.addTarget(self, action: #selector(self.middleButtonAction), for: .touchUpInside)
self.view.layoutIfNeeded()
}
// Complete button
func setupCompleteMiddleButton() {
let middleButton = UIButton(frame: CGRect(x: (self.view.bounds.width / 2) - 25, y: -20, width: 50, height: 50))
middleButton.backgroundColor = UIColor.systemGreen
middleButton.setImage(UIImage(systemName: "checkmark"), for: .normal)
middleButton.imageView?.tintColor = UIColor.white
middleButton.layer.cornerRadius = middleButton.frame.width / 2
middleButton.clipsToBounds = true
self.tabBar.addSubview(middleButton)
middleButton.addTarget(self, action: #selector(self.middleButtonAction), for: .touchUpInside)
self.view.layoutIfNeeded()
}
#objc func middleButtonAction(sender: UIButton) {
self.selectedIndex = 1
}
}
Thank you!
You can try this for globally single object of button and just change the image whenever task is complete or not.
class TabBarController: UITabBarController, UITabBarControllerDelegate {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
}
// TabbarController hode this button
var middleButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
setupButton()
// This is where I currently decide which button to show the "complete" one if the task is done, and the incomplete one if not.
self.delegate = self
// taskCompletion is a call back when UserData finish its task
if UserData.taskCompletion {
setupCompleteMiddleButton()
} else {
setupIncompleteMiddleButton()
}
}
func setupButton() {
middleButton = UIButton(frame: CGRect(x: (self.view.bounds.width / 2) - 25, y: -20, width: 50, height: 50))
middleButton.backgroundColor = UIColor.systemYellow
middleButton.layer.cornerRadius = middleButton.frame.width / 2
middleButton.clipsToBounds = true
self.tabBar.addSubview(middleButton)
middleButton.addTarget(self, action: #selector(self.middleButtonAction), for: .touchUpInside)
self.view.layoutIfNeeded()
}
// Incomplete button
func setupIncompleteMiddleButton() {
middleButton.backgroundColor = UIColor.systemYellow
middleButton.setImage(UIImage(systemName: "sun.max.fill"), for: .normal)
}
// Complete button
func setupCompleteMiddleButton() {
// change button color and image
middleButton.backgroundColor = UIColor.systemGreen
middleButton.setImage(UIImage(systemName: "checkmark"), for: .normal)
}
#objc func middleButtonAction(sender: UIButton) {
self.selectedIndex = 1
}
Maybe you can try this:
import UIKit
class TabBarController: UITabBarController, UITabBarControllerDelegate {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
}
// TabbarController hode this button
var middleButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// This is where I currently decide which button to show the "complete" one if the task is done, and the incomplete one if not.
self.delegate = self
// taskCompletion is a call back when UserData finish its task
if UserData.taskCompletion {
setupCompleteMiddleButton()
}
setupIncompleteMiddleButton()
}
// Incomplete button
func setupIncompleteMiddleButton() {
// initialize button
middleButton = UIButton(frame: CGRect(x: (self.view.bounds.width / 2) - 25, y: -20, width: 50, height: 50))
middleButton.backgroundColor = UIColor.systemYellow
middleButton.setImage(UIImage(systemName: "sun.max.fill"), for: .normal)
middleButton.imageView?.tintColor = UIColor.white
middleButton.layer.cornerRadius = middleButton.frame.width / 2
middleButton.clipsToBounds = true
self.tabBar.addSubview(middleButton)
middleButton.addTarget(self, action: #selector(self.middleButtonAction), for: .touchUpInside)
self.view.layoutIfNeeded()
}
// Complete button
func setupCompleteMiddleButton() {
// change button color and image
middleButton.backgroundColor = UIColor.systemGreen
middleButton.setImage(UIImage(systemName: "checkmark"), for: .normal)
}
#objc func middleButtonAction(sender: UIButton) {
self.selectedIndex = 1
}
}

Swift: Use of unresolved identifier 'present'

I have a UIView added to UIViewController, and some code of them
ViewController.swift
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let listView = ListView()
self.view.addSubview(listView)
}
ListView.swift
class ListView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
self.addSubview(btnGO)
}
convenience init() {
self.init(frame: CGRect.zero)
}
required init(coder aDecoder: NSCoder) {
fatalError("This class does not support NSCoding")
}
lazy var btnGO:(UIButton) = {
let btn = UIButton(frame: CGRect(x: 0, y: 0, width: 44, height: 34))
btn.addTarget(self, action: #selector(btnClicked), for: .touchUpInside)
//some codes
return btn
}()
#objc func btnClicked(sender:UIButton) {
let shareWOW = "some content"
let objectsToShare = [shareWOW]
let activityController = UIActivityViewController(
activityItems: objectsToShare,
applicationActivities: nil)
activityController.popoverPresentationController?.sourceRect = self.frame
activityController.popoverPresentationController?.sourceView = self
activityController.popoverPresentationController?.permittedArrowDirections = .any
present(activityController, animated: true, completion: nil)
}
}
I always get error information Use of unresolved identifier 'present', and I know UIActivityViewController should be placed in UIViewController, and my question is I place the function btnClicked in ViewController.swift, how to call this function in a UIView as ListView.swift? Many thanks
present(_:animated:completion:) method should be used with UIViewController instance. You can't call this method in a UIView subclass. Use delegate to call a method in ViewController from ListView and present UIActivityViewController from the ViewController like this
ViewController.swift
class ViewController: UIViewController, ListViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let listView = ListView()
listView.delegate = self
self.view.addSubview(listView)
}
func btnClicked(sender: UIButton) {
let shareWOW = "some content"
let objectsToShare = [shareWOW]
let activityController = UIActivityViewController(
activityItems: objectsToShare,
applicationActivities: nil)
activityController.popoverPresentationController?.sourceRect = sender.frame
activityController.popoverPresentationController?.sourceView = sender
activityController.popoverPresentationController?.permittedArrowDirections = .any
self.present(activityController, animated: true, completion: nil)
}
}
ListView.swift
protocol ListViewDelegate {
func btnClicked(sender:UIButton)
}
class ListView: UIView {
var delegate: ListViewDelegate?
override init(frame: CGRect) {
super.init(frame: frame)
self.addSubview(btnGO)
}
convenience init() {
self.init(frame: CGRect.zero)
}
required init(coder aDecoder: NSCoder) {
fatalError("This class does not support NSCoding")
}
lazy var btnGO:(UIButton) = {
let btn = UIButton(frame: CGRect(x: 0, y: 0, width: 44, height: 34))
btn.addTarget(self, action: #selector(btnClicked), for: .touchUpInside)
//some codes
return btn
}()
#objc func btnClicked(sender:UIButton) {
delegate?.btnClicked(sender: sender)
}
}

UITextField Custom Clear Button

I'm trying to implement custom clear button in text field using the solution on Custom Clear Button
It doesn't work, it shows default clear button. Any idea why? Following is my code:
class CustomTextField: UITextField {
override init(frame: CGRect) {
super.init(frame: frame)
let clearButton = UIButton(frame: CGRect(x: 0, y: 0, width: 16, height: 16))
clearButton.setImage(UIImage(named: "Glyph/16x16/Clear")!, for: [])
self.rightView = clearButton
clearButton.addTarget(self, action: #selector(clearClicked), for: .touchUpInside)
self.clearButtonMode = .never
self.rightViewMode = .whileEditing
}
#objc override func clearClicked(sender:UIButton)
{
self.text = ""
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
As mentioned already, in your code the clearClicked method should not override as UITextField doesn't have a clearClicked method to override.
Anyway, I updated the code to work when using it with storyboards. Added the awakeFromNib method which calls the initialisation code.
class CustomTextField: UITextField {
override open func awakeFromNib() {
super.awakeFromNib()
self.initialize()
}
override init(frame: CGRect) {
super.init(frame: frame)
self.initialize()
}
func initialize() {
let clearButton = UIButton(frame: CGRect(x: 0, y: 0, width: 16, height: 16))
clearButton.setImage(UIImage(named: "Glyph/16x16/Clear")!, for: [])
self.rightView = clearButton
clearButton.addTarget(self, action: #selector(clearClicked), for: .touchUpInside)
self.clearButtonMode = .never
self.rightViewMode = .whileEditing
}
#objc func clearClicked(sender:UIButton)
{
self.text = ""
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}

how to prevent zooming of parantView while zooming of imageView

i have created CustomeView that contain Scroll-view.inside scroll view there is one container view that contain image view plus two button(Okay and cancel).
Following is my view-hierarchy.
CustomeView -> ScrollView -> ContainerView -> (imageView + OtherComponent).
There are two problem i faced.
while zoom in-out imageview,CustomeView is also zoomed in-out with
respect to Scrollview.
other component postion is changed while zoom in-out.
class cameraPreview : UIView , UIScrollViewDelegate {
var selectedImage : UIImage!
var backGroundView = UIView()
var imageScrollview = UIScrollView()
var metaData : [String:Any]?
let backgroundImageView = UIImageView()
var closeButton : UIButton = {
let button = UIButton(type: UIButtonType.custom)
button.setImage(UIImage(named:"closeWhite"), for: .normal)
button.addTarget(self, action: #selector(closeClick), for: .touchUpInside)
return button
}()
var okButton : UIButton = {
let button = UIButton()
button.setTitle("OK", for: .normal)
button.titleLabel?.font = UIFont.systemFont(ofSize: 15)
button.setTitleColor(UIColor.black, for: .normal)
button.backgroundColor = UIColor.white
button.layer.cornerRadius = 15
button.addTarget(self, action: #selector(okClick), for: .touchUpInside)
return button
}()
override init(frame: CGRect) {
super.init(frame: frame)
commoninit()
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commoninit()
}
required init(image:UIImage,frame:CGRect,metaData:[String:Any]?) {
super.init(frame: frame)
self.selectedImage = image
self.metaData = metaData
commoninit()
}
override func layoutSubviews() {
}
func commoninit() {
backGroundView.backgroundColor = UIColor.white
backgroundImageView.contentMode = .scaleAspectFit
self.addSubview(imageScrollview)
imageScrollview.addSubview(backGroundView)
backGroundView.addSubview(backgroundImageView)
backGroundView.addSubview(closeButton)
backGroundView.addSubview(okButton)
imageScrollview.snp.makeConstraints { (make) in
make.edges.equalTo(self)
}
backGroundView.snp.makeConstraints { (make) in
make.edges.equalTo(imageScrollview)
make.height.width.equalTo(self)
}
backgroundImageView.snp.makeConstraints { (make) in
make.edges.equalTo(backGroundView)
}
okButton.snp.makeConstraints { (make) in
make.width.equalTo(80)
make.height.equalTo(30)
make.centerX.equalTo(backGroundView.snp.centerX)
make.bottom.equalTo(backGroundView).offset(-20)
}
closeButton.snp.makeConstraints { (make) in
make.width.height.equalTo(30)
make.left.equalTo(20)
make.top.equalTo(10)
}
backgroundImageView.image = selectedImage
imageScrollview.delegate = self
imageScrollview.minimumZoomScale = 1.0
imageScrollview.maximumZoomScale = 6.0
}
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return backgroundImageView
}
#objc func closeClick(sender:UIButton) {
self.removeFromSuperview()
}
#objc func okClick(sender:UIButton) {
if let topCotroller = UIApplication.shared.gettopMostViewController() {
self.removeFromSuperview()
let mediaDetailController = UploadDetailsViewController.instantiate(fromAppStoryboard: .Upload)
mediaDetailController.mediaImage = selectedImage
if metaData != nil {
mediaDetailController.exifDictionary = metaData![kCGImagePropertyExifDictionary as String] as? [String : AnyObject]
}
topCotroller.navigationController?.pushViewController(mediaDetailController, animated: true)
}
}
}
Following is code to add cameraPreview inside current ControllerView
let imagePreview = cameraPreview(image: image, frame: UIScreen.main.bounds,metaData:metaData)
self.view.addSubview(imagePreview)
your other component is zoom in-out because you put those component inside ScrollView.if you simply put those component out side of ScrollView than your component will not zoom in-out with respect to ScrollView.
Following is Source Code.
class cameraPreview : UIView , UIScrollViewDelegate {
var selectedImage : UIImage!
var backGroundView = UIView()
var imageScrollview = UIScrollView()
var metaData : [String:Any]?
let backgroundImageView = UIImageView()
var closeButton : UIButton = {
let button = UIButton(type: UIButtonType.custom)
button.setImage(UIImage(named:"closeWhite"), for: .normal)
button.addTarget(self, action: #selector(closeClick), for: .touchUpInside)
return button
}()
var okButton : UIButton = {
let button = UIButton()
button.setTitle("OK", for: .normal)
button.titleLabel?.font = UIFont.systemFont(ofSize: 15)
button.setTitleColor(UIColor.black, for: .normal)
button.backgroundColor = UIColor.white
button.layer.cornerRadius = 15
button.addTarget(self, action: #selector(okClick), for: .touchUpInside)
return button
}()
override init(frame: CGRect) {
super.init(frame: frame)
commoninit()
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commoninit()
}
required init(image:UIImage,frame:CGRect,metaData:[String:Any]?) {
super.init(frame: frame)
self.selectedImage = image
self.metaData = metaData
commoninit()
}
override func layoutSubviews() {
}
func commoninit() {
self.backgroundColor = UIColor.white
backGroundView.backgroundColor = UIColor.clear
backgroundImageView.contentMode = .scaleAspectFit
self.addSubview(imageScrollview)
imageScrollview.addSubview(backGroundView)
backGroundView.addSubview(backgroundImageView)
self.addSubview(closeButton)
self.addSubview(okButton)
imageScrollview.snp.makeConstraints { (make) in
make.edges.equalTo(self)
}
backGroundView.snp.makeConstraints { (make) in
make.edges.equalTo(imageScrollview)
make.height.width.equalTo(self)
}
backgroundImageView.snp.makeConstraints { (make) in
make.edges.equalTo(backGroundView)
}
okButton.snp.makeConstraints { (make) in
make.width.equalTo(80)
make.height.equalTo(30)
make.centerX.equalTo(self)
make.bottom.equalTo(self).offset(-20)
}
closeButton.snp.makeConstraints { (make) in
make.width.height.equalTo(30)
make.left.equalTo(20)
make.top.equalTo(10)
}
backgroundImageView.image = selectedImage
imageScrollview.delegate = self
imageScrollview.minimumZoomScale = 1.0
imageScrollview.maximumZoomScale = 6.0
}
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return backgroundImageView
}
#objc func closeClick(sender:UIButton) {
self.removeFromSuperview()
}
#objc func okClick(sender:UIButton) {
if let topCotroller = UIApplication.shared.gettopMostViewController() {
self.removeFromSuperview()
let mediaDetailController = UploadDetailsViewController.instantiate(fromAppStoryboard: .Upload)
mediaDetailController.mediaImage = selectedImage
if metaData != nil {
mediaDetailController.exifDictionary = metaData![kCGImagePropertyExifDictionary as String] as? [String : AnyObject]
}
topCotroller.navigationController?.pushViewController(mediaDetailController, animated: true)
}
}
}

"Fatal Error: init(coder:) has not been implemented" error in my iOS app

I'm implementing the SwiftyCam pod in my iOS 10/ XCode8.2 app. It's a custom cameraviewcontroller pod, it seems really simple to use and implement, but when I run it it gives me this error. Can you guys help me? I already looked at other questions, using their lines of codes that worked for them, but nada, none of them worked for me.
Here's the code:
class randomController: SwiftyCamViewController, SwiftyCamViewControllerDelegate, UIImagePickerControllerDelegate,UINavigationControllerDelegate, CAPSPageMenuDelegate, AVCapturePhotoCaptureDelegate {
private var backgroundImage: UIImage
init(image: UIImage) {
self.backgroundImage = image
super.init(nibName: nil, bundle: nil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.gray
let backgroundImageView = UIImageView(frame: view.frame)
backgroundImageView.image = backgroundImage
view.addSubview(backgroundImageView)
let cancelButton = UIButton(frame: CGRect(x: 10.0, y: 10.0, width: 30.0, height: 30.0))
cancelButton.setImage(#imageLiteral(resourceName: "cancel"), for: UIControlState())
cancelButton.addTarget(self, action: #selector(cancel), for: .touchUpInside)
view.addSubview(cancelButton)
}
func cancel() {
dismiss(animated: true, completion: nil)
}
}

Resources