I have added a UISwitch to my Navigation Controller. This switch controls the underlying tableview and toggles it between 2 data sources. However, when i tried adding IBOutlets and Actions the suggested type came up as UIBarButtonItem, nevertheless when i saved it as a UISwitch, so i could have the .on boolean. However, it seems that the switch is not doing anything. When i click it nothing is happening and Im not sure as to why. My code below.
#IBAction func PreferencesToggled(sender: UIButton) {
if DataToggle.on
{
let defaults = NSUserDefaults.standardUserDefaults()
if let prefs = defaults.objectForKey("teachPref"){
data = prefs as! [newsarticle]
self.tableView.reloadData()
}
else{
data = [newsarticle]()
data.append(newsarticle(name: "No Teachers Saved",desc: "http://www.google.com"))
}
let footer = UIView()
self.tableView.tableFooterView = footer
}
else{
self.tableView.tableFooterView = nil
getdata()
}
}
This is how to add a switch programmatically to your navigtion bar:
let switchView = UISwitch()
switchView.addTarget(self, action: #selector(self.PreferencesToggled(_:)), forControlEvents: .ValueChanged)
//customize your switch here
let barBtn = UIBarButtonItem(customView: switchView)
self.navBar?.topItem?.setRightBarButtonItem(barBtn, animated: false)
Related
I am trying to create a custom cell in Eureka that display an Image. When tap the image, the image displays full screen with black background.
Usually you do it with view.addSubview(newImageView), but it does not seem like Eureka cell has view class.
Here is what I got so far for the cell:
final class ImageViewCell: Cell<ImageView>, CellType {
#IBOutlet weak var viewImage: UIImageView!
//let storage = Storage.storage().reference()
required init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func setup() {
super.setup()
//cell not selectable
selectionStyle = .none
viewImage.contentMode = .scaleAspectFill
viewImage.clipsToBounds = true
height = {return 300 }
//make userImage reconize tapping
let tapRec = UITapGestureRecognizer(target: self, action: #selector(imageTapHandler(tapGestureRecognizer:)))
viewImage.addGestureRecognizer(tapRec)
viewImage.isUserInteractionEnabled = true
}
#objc func imageTapHandler(tapGestureRecognizer: UITapGestureRecognizer) {
let imageView = tapGestureRecognizer.view as! UIImageView
let newImageView = UIImageView(image: imageView.image)
newImageView.frame = UIScreen.main.bounds
newImageView.backgroundColor = .black
newImageView.contentMode = .scaleAspectFit
newImageView.isUserInteractionEnabled = true
let tap = UITapGestureRecognizer(target: self, action: #selector(dismissFullscreenImage))
newImageView.addGestureRecognizer(tap)
view.addSubview(newImageView)
}
#objc func dismissFullscreenImage(_ sender: UITapGestureRecognizer) {
sender.view?.removeFromSuperview()
}
override func update() {
super.update()
// we do not want to show the default UITableViewCell's textLabel
textLabel?.text = nil
// get the value from our row
guard let imageData = row.value else { return }
// get user image data from value
let downloadData = imageData.pictureData
viewImage.image = UIImage(data: downloadData)
}
}
I am getting "Use of unresolved identifier 'view'" when trying to addSubview.
I have tried to use contentView instead of view, and the result is like this:
Screenshot for the View
If you want to show the image in fullScreen, cell's contentView or the viewController subView that holds this cell are not the right places to add this image as subview.
A proper solution to this is to use the onCellSelection(something as shown below) callback in your container ViewController and present a new ViewController that display's only image fullscreen or whatever customization you want.
imageCell.onCellSelection({[weak self] (cell, row) in
let imageVC = DisplayViewController()
imageVC.image = cell.viewImage.image
self?.present(imageVC, animated: true, completion: nil)
})
If you want to show fullscreen only when user taps the image in cell then you should get the tapGesture callback in container ViewController and show the image as above.
Thanks for Kamran, I think I found the solution.
for Eureka rows, you can always use row.onCellSelection call back.
in my case I can do this when calling the custom row:
<<< ImageViewCellRow(){ row in
row.value = ImageView(pictureData: data!)
row.onCellSelection(showMe)
}
then create a showMe function like this:
func showMe(cell: ImageViewCell, row: (ImageViewCellRow)) {
print("tapped my ImageViewCell!")
}
now you should be able to present a full screen image as Kamran's code.
In my app, I have a toolbar with UIBarButtonItems.
In most circumstances, the UIBarButtonItems are set via storyboard, and look as follows:
In a special case, I have to replace one UIBarButtonItem programmatically. This is done with the following code:
let rotatingButton = UIButton(type: .custom)
rotatingButton.setImage(UIImage(named: "LocalizationInUseNoFix"), for: .normal)
rotatingButton.addTarget(self, action: #selector(localizationButtonTapped), for: .touchUpInside)
rotatingButton.rotateStart()
let barButtonItem = UIBarButtonItem(customView: rotatingButton)
leftBarButtonItems![2] = barButtonItem
When the rotatingButton is displayed in the toolbar, it placed at a different position. It is shifted to the right, as you can see here:
How can I achieve to place both UIBarButtonItems at the same position?
EDIT:
By now I realized that the horizontal shift of the programmatically created UIBarButtonItem is not always the same, without any changes to the code: Sometimes it is shifted left, and not right:
EDIT 2:
I found a workaround:
If I set a width constrain to my button like
rotatingButton.widthAnchor.constraint(equalToConstant: 40).isActive = true
then the button is apparently always correctly placed. But I hate to hard-code constraints like this.
Is there a more elegant way to do it?
Try the below steps to perform your task:
Store left bar button items into an NSMutableArray
Replace desired UIBarbuttonItem
Set leftbarbuttonitems to this new array
Hope this steps will work
When you set the image on UIBarButton programmatically, the contentmode of the leftBarButtonItems becomes 'left' and rightBarButtonItems become 'right'. But from storyboard, it is centered. Set the image and adjust the contentMode as required.
All are working fine for Navigationbar and Toolbar
class ViewController: UIViewController {
#IBOutlet weak var toolbar: UIToolbar!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func leftAction(_ sender: Any) {
}
#IBAction func rightAction(_ sender: Any) {
}
#IBAction func changeLeftItems(_ sender: Any) {
if let items = self.navigationItem.leftBarButtonItems {
var addItems = [UIBarButtonItem]()
addItems.append(contentsOf: items)
let barItem = UIBarButtonItem(title: "3", style: UIBarButtonItemStyle.plain, target: self, action: #selector(ViewController.leftAction(_:)))
addItems.append(barItem)
self.navigationItem.leftBarButtonItems = addItems
}
if let items = self.toolbar.items {
var addItems = [UIBarButtonItem]()
addItems.append(contentsOf: items)
let barItem = UIBarButtonItem(title: "L3", style: UIBarButtonItemStyle.plain, target: self, action: #selector(ViewController.leftAction(_:)))
addItems.insert(barItem, at: 2)
self.toolbar.setItems(addItems, animated: true)
}
}
}
This is the best solution I found so far:
Get the width of a view of another bar button item using key value coding. This is from Jeremy W. Sherman’s answer here.
Please note that it does not use any private API, see the discussion there. The worst thing that can happen is that the view property of the UIBarButtonItem cannot be accessed. In this case, I use a default value:
var leftBarButtonItems = self.navigationItem.leftBarButtonItems
let rotatingButton = UIButton(type: .custom)
rotatingButton.setImage(UIImage(named: "LocalizationInUseNoFix"), for: .normal)
rotatingButton.addTarget(self, action: #selector(localizationButtonTapped), for: .touchUpInside)
rotatingButton.rotateStart()
// Get the width of the bar button items if possible, else set default
let leftmostBarButtonItem = leftBarButtonItems![0]
let barButtonItemWidth: CGFloat
if let leftmostBarButtonItemView = leftmostBarButtonItem.value(forKey: "view") as? UIView {
barButtonItemWidth = leftmostBarButtonItemView.frame.size.width
} else {
barButtonItemWidth = 40.0
}
rotatingButton.widthAnchor.constraint(equalToConstant: barButtonItemWidth).isActive = true
let barButtonItem = UIBarButtonItem(customView: rotatingButton)
leftBarButtonItems![2] = barButtonItem
self.navigationItem.leftBarButtonItems = leftBarButtonItems
This is working fine for me. Best way is identify item to replace and change the content
#IBAction func changeLeftItems(_ sender: Any) {
if let items = self.toolbar.items {
var addItems = [UIBarButtonItem]()
addItems.append(contentsOf: items)
let barItem = UIBarButtonItem(title: "L5", style: UIBarButtonItemStyle.plain, target: self, action: #selector(ViewController.leftAction(_:)))
addItems.remove(at: 1)
addItems.insert(barItem, at: 1)
self.toolbar.setItems(addItems, animated: true)
}
}
first post so apologies if I mess something up. I have researched this for hours upon hours and read other posts here on stack exchange to no avail.
I have created a nib file that defines a custom view and have defined a custom class (UIView) to manage the outlets of the custom view. As you can see from the code below excerpted from my custom UIView class associated with the nib, I have a date picker as the input view for the custom class and a UIToolBar with two UIBarButtonItems. Both of these appear as desired through a tap gesture recognizer... however the problem is the UIBarButtonItems do not call the action when tapped. Placing a breakpoint in the action function reveals that the code is never run. I feel that something with the view lifecycle is preventing a reference from being made, but I am new to Swift so some help here would be appreciated. I don't think it is selector syntax as the tap gesture recognizer works as desired. I've tried messing with button click handling access levels. I've tried doing input view setup when the view awakes from the nib as well, along with trying to put the code in different parts of the lifecycle.
If it matters for lifecycle's sake, this nib is a part of a table view cell. I call for this nib to be loaded when the table view cell awakes from it's nib.
Thanks!
#IBOutlet weak var timerStackView: UIStackView!{
didSet{
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(HandleTap(_:)))
timerStackView.addGestureRecognizer(tapGesture)
}
}
var datePicker: UIDatePicker {
let picker = UIDatePicker()
picker.backgroundColor = UIColor.black
picker.isOpaque = false
picker.setValue(UIColor.white, forKey: "textColor")
return picker
}
var datePickerAccessoryView: UIToolbar {
let accessoryView = UIToolbar()
let doneButton = UIBarButtonItem(title: "Done", style: UIBarButtonItemStyle.done, target: self, action: #selector(handleDatePickerButtonClick(_:)))
doneButton.tintColor = UIColor.white
let cancelButton = UIBarButtonItem(title: "Cancel", style: UIBarButtonItemStyle.plain, target: self, action: #selector(handleDatePickerButtonClick(_:)))
cancelButton.tintColor = UIColor.white
accessoryView.setItems([cancelButton, doneButton], animated: true)
return accessoryView
}
override var inputView: UIView? {return datePicker}
override var inputAccessoryView: UIView? {return datePickerAccessoryView}
override var canBecomeFirstResponder: Bool {return true}
override var canResignFirstResponder: Bool {return true}
// MARK: - Private functions
#objc fileprivate func HandleTap(_ sender: UITapGestureRecognizer) -> Void {
if !self.isFirstResponder {
switch sender.state {
case .ended:
datePicker.date = Date()
self.becomeFirstResponder()
default:
break
}
}
}
#objc #IBAction internal func handleDatePickerButtonClick(_ sender: UIBarButtonItem) -> Void {
switch sender.title! {
case "Done":
// To be implemented
case "Cancel":
// To be implemented
default:
break
}
}
You are initialising the UIToolbar without a frame and that would make it not register any touch events because they would be out of the toolbar's bounds.
Replace let accessoryView = UIToolbar() with something like let accessoryView = UIToolbar(frame: CGRect(x: 0, y: 0, width: view.bounds.width, height: 44))
Or you can call accessoryView.sizeToFit() before return accessoryView
I suppose the tap gesture recognizer is interfering with native UIBarButtonItem click events. But why do you use a gesture recognizer for that?
You should better add an action to each particular UIBarButtonItem.
I have the following piece of code. It's a third party library for a menu (named CarbonKit). When I try to select a specific segment (tab) and add a gesture recognizer, it doesn't work. Any ideas what I'm doing wrong?
To be clear, I placed a breakpoint in the handleTap, it it doesn't even enter the function.
override func viewDidLoad() {
super.viewDidLoad()
self.view.userInteractionEnabled = true
let tgr : UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(OverviewFolder.handleTap(_:)))
// segment 2 (categories)
carbonTabSwipeNavigation.carbonSegmentedControl?.segments![2].userInteractionEnabled = true
carbonTabSwipeNavigation.carbonSegmentedControl?.segments![2].addGestureRecognizer(tgr)
}
// tap
func handleTap(gestureRecognizer : UITapGestureRecognizer){
let test = carbonTabSwipeNavigation.currentTabIndex
if test == 2 {
print("second item tapped")
}
}
If the 3rd party UISegmentedControl is like the generic one, you already have everything you need. Here's some of my code. If you are using IB, wire the control up to an IBAction instead.
let imageSegments = UISegmentedControl (items: ["Original","Stained"])
override func viewDidLoad() {
super.viewDidLoad()
imageSegments.tintColor = UIColor.yellow
imageSegments.selectedSegmentIndex = 1
imageSegments.addTarget(self, action: #selector(changeImageView), for: .valueChanged)
view.addSubview(imageSegments)
}
func changeImageView() {
switch imageSegments.selectedSegmentIndex {
case 0:
imageView.image = imgOriginal
case 1:
imageView.image = imgEdited
default:
break
}
imageView.setNeedsDisplay()
}
There is a Save (System Item) on my navigation bar as BarButtonItem I am showing UIActivityIndicatorView on the navigation bar when user clicks this Save Button and I want to appear this Barbutton(Save) again on certain condition. First I think the problem is I am adding a indicator on customView so I don't need to hide the barbutton.It automatically hides itself after I start the indicator. But don't know now how to show Save Button again. or how can I remove the indicator from customView
This is how I am doing
#IBOutlet weak var saveButtonOutlet: UIBarButtonItem!
var activityIndicatorView:UIActivityIndicatorView!
func showActivityIndicator() {
activityIndicatorView = UIActivityIndicatorView(activityIndicatorStyle: UIActivityIndicatorViewStyle.White)
activityIndicatorView.frame = CGRectMake(0, 0, 14, 14)
activityIndicatorView.color = UIColor().blueColorIOS()
activityIndicatorView.startAnimating()
let barButtonItem = UIBarButtonItem(customView: activityIndicatorView)
self.navigationItem.rightBarButtonItem = barButtonItem
}
#IBAction func saveButtonClicked(sender: UIBarButtonItem) {
showActivityIndicator()
ServerRequest.postToServer(url, params: params){
result, error in
if let result = result {
let code = result["code"] as? Int
print(result)
if (code==200){
dispatch_after(DISPATCH_TIME_NOW, dispatch_get_main_queue(), { ()->() in
self.activityIndicatorView.hidden = true
self.activityIndicatorView.hidesWhenStopped = true
//here want to show again "saveButtonOutlet"
})
}
}
}
}
}
So one way to do this is to create the Save button again, and setting the rightBarButtonItem again:
...
self.activityIndicatorView.hidden = true
self.activityIndicatorView.hidesWhenStopped = true
let barButtonItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.Save, target: self, action: "saveButtonClicked:")
self.navigationItem.rightBarButtonItem = barButtonItem
And I'd also replace self.activityIndicatorView.hidden = true with self.activityIndicatorView.stopAnimating() to properly use the hidesWhenStopped property.
I think all you need to do is reset the self.navigationItem.rightBarButtonItem to the saveButtonOutlet.
Worked right now for me.