I've got the following code:
[[UIApplication sharedApplication] sendAction:#selector(copy:) to:nil from:self forEvent:nil];
NSString *string = [UIPasteboard generalPasteboard].string;
The problem is that the pasteboard is returning what I've copied previously, not currently. Somehow it's hanging onto the previous item I've copied and returning that, and then next time around I get the item that I wanted and copied... strange behaviour.. any ideas?
Edit
I've got a solution using an observer, but not sure how elegant it is.. there seems to be some delay between copy and setting an item.. enough for it not to be set and for the previous one to be still on the UIPasteBoard.
I have composed a little sample to your code for copy/paste behavior and it works as expected: selected text is printed to console after tapping on the button.
class ViewController: UIViewController {
let textField = UITextField()
let copyButton = UIButton(type: .roundedRect)
#objc func copySelectedText() {
UIApplication.shared.sendAction(#selector(UIResponder.copy(_:)), to: nil, from: self, for: nil)
let copiedString = UIPasteboard.general.string
print(copiedString)
}
override func viewDidLoad() {
super.viewDidLoad()
textField.borderStyle = .roundedRect
view.backgroundColor = .darkGray
display(textField, copyButton)
copyButton.setTitle("tap to copy selected text", for: .normal)
copyButton.addTarget(self, action: #selector(copySelectedText), for: .touchUpInside)
}
private func display(_ textField: UITextField, _ copyButton: UIButton) {
copyButton.translatesAutoresizingMaskIntoConstraints = false
textField.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(copyButton)
view.addSubview(textField)
textField.backgroundColor = .white
textField.widthAnchor.constraint(greaterThanOrEqualToConstant: 200).isActive = true
textField.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
textField.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
copyButton.topAnchor.constraint(equalTo: textField.bottomAnchor, constant: 20).isActive = true
copyButton.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
}
}
Related
Similar question to :
Disable gesture to pull down form/page sheet modal presentation
Looking for functionality exactly like this, except with a tableview under the navigation controller:
when I attempt to use the answer given, touchesBegan and touchesEnded do not get called, so I tried calling the functions in scrollViewWillBeginDragging and scrollViewWillEndDragging and while it does get called, it doesnt solve the issue. Scrolling the tableview will still close the modal most of the time.
I do not want to present over full screen.
isModalInPresentation stops it from closing but the bounce down still attempts. I want to stop that altogether.
I want to be able to only close if the user presses cancel or actually grabs the navigation bar to close like in the gif, and swipes within the tableview to only scroll the tableview. What else can I try?
If I understand well, this is an example to do it programmatically... Declare button under your controller class:
let myButton: UIButton = {
let b = UIButton()
b.backgroundColor = .black
b.setTitle("Tap me!", for: .normal)
b.setTitleColor(.white, for: .normal)
b.titleLabel?.font = .systemFont(ofSize: 17, weight: .regular)
b.layer.cornerRadius = 12
b.clipsToBounds = true
b.translatesAutoresizingMaskIntoConstraints = false
return b
}()
Now in viewDidLoad add target and set constraints:
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
myButton.addTarget(self, action: #selector(callSeetController), for: .touchUpInside)
view.addSubview(myButton)
myButton.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
myButton.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
myButton.heightAnchor.constraint(equalToConstant: 50).isActive = true
myButton.widthAnchor.constraint(equalToConstant: 200).isActive = true
}
After that call the func to present an set sheet attributes:
#objc fileprivate func callSeetController() {
let detailViewController = SecondController()
let nav = UINavigationController(rootViewController: detailViewController)
nav.isModalInPresentation = true // disable swipe down
if let sheet = nav.sheetPresentationController {
sheet.detents = [.large()]
}
let image = UIImage(systemName: "x.circle")
let dismiss = UIBarButtonItem(image: image, primaryAction: .init(handler: { [weak self] _ in
if let sheet = nav.sheetPresentationController {
sheet.animateChanges {
self?.dismiss(animated: true, completion: nil)
}
}
}))
detailViewController.navigationItem.rightBarButtonItem = dismiss
detailViewController.navigationController?.navigationBar.tintColor = .white
present(nav, animated: true, completion: nil)
}
The result:
I have MainViewController and DetailViewController that are stacked together by a navigation controller. I want to pass a value from DetailViewController back to the previous controller, which is MainViewController.
First, I tried it with UINavigationControllerDelegate:
class DetailViewController: UINavigationControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.delegate = self
}
func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
(viewController as? MainViewController)?.myClass = myClass
}
}
which was to be called as DetailViewController is popped:
_ = navigationController?.popViewController(animated: true)
But, the new value doesn't get reflected on MainViewController:
class MainViewController: UIViewController {
var myClass: MyClass
private lazy var commentLabel: UILabel = {
let comment = UILabel()
comment.text = myClass.comment
comment.numberOfLines = 0
return comment
}()
}
even though when I log myClass in MainViewController, I can see that it's being passed properly.
I also tried it with a property observer so that DetailViewController can pass it to a temporary property observer instead:
var temp: MyClass? {
willSet(newValue) {
myClass = newValue
}
}
but, the view controller's interface still doesn't change.
Finally, I tried creating a delegate in MainViewController:
protocol CallBackDelegate {
func callBack(value: MyClass)
}
where the function simply passes the argument:
func callBack(value: MyClass) {
myClass = value
}
I set the delegate to self:
if let vc = self.storyboard?.instantiateViewController(withIdentifier: "Detail") as? DetailViewController {
vc.delegate = self
self.navigationController?.pushViewController(vc, animated: true)
}
and invoking the function in DetailViewController:
delegate?.callBack(value: MyClass)
but, still doesn't update the interface. It seems as though passing the value isn't the issue, but having it be reflected is.
This is an example of using the protocol / delegate pattern. It's about as basic as it gets...
Start a new single-view project
add the code below
Set the class of the default view controller to MainViewController
embed it in a Navigation Controller
run the app
Then:
Tap the button labeled "Push to next VC"
Enter some text in the "Edit Me" field
Tap the "Pop back to previous VC"
See that the label has been updated with your entered text.
protocol CallBackDelegate: class {
func callback(_ val: String)
}
class MainViewController: UIViewController, CallBackDelegate {
let btn = UIButton()
let theLabel = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
btn.translatesAutoresizingMaskIntoConstraints = false
theLabel.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(btn)
view.addSubview(theLabel)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
btn.topAnchor.constraint(equalTo: g.topAnchor, constant: 100.0),
btn.centerXAnchor.constraint(equalTo: g.centerXAnchor),
theLabel.topAnchor.constraint(equalTo: btn.bottomAnchor, constant: 20.0),
theLabel.centerXAnchor.constraint(equalTo: g.centerXAnchor),
])
theLabel.backgroundColor = .yellow
btn.backgroundColor = .red
theLabel.text = "Default text"
btn.setTitle("Push to next VC", for: [])
btn.addTarget(self, action: #selector(self.pushButtonTapped(_:)), for: .touchUpInside)
}
#objc func pushButtonTapped(_ sender: Any?) -> Void {
let vc = DetailViewController()
vc.delegate = self
self.navigationController?.pushViewController(vc, animated: true)
}
func callback(_ val: String) {
theLabel.text = val
}
}
class DetailViewController: UIViewController {
weak var delegate: CallBackDelegate?
let textField = UITextField()
let btn = UIButton()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
btn.translatesAutoresizingMaskIntoConstraints = false
textField.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(btn)
view.addSubview(textField)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
textField.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
textField.centerXAnchor.constraint(equalTo: g.centerXAnchor),
textField.widthAnchor.constraint(equalToConstant: 200.0),
btn.topAnchor.constraint(equalTo: textField.bottomAnchor, constant: 20.0),
btn.centerXAnchor.constraint(equalTo: g.centerXAnchor),
])
textField.backgroundColor = .yellow
textField.borderStyle = .roundedRect
btn.backgroundColor = .blue
textField.placeholder = "Edit me"
btn.setTitle("Pop back to previous VC", for: [])
btn.addTarget(self, action: #selector(self.popButtonTapped(_:)), for: .touchUpInside)
}
#objc func popButtonTapped(_ sender: Any?) -> Void {
if let s = textField.text {
delegate?.callback(s)
}
self.navigationController?.popViewController(animated: true)
}
}
Doesn't seem that you are updating the UILabel value anyhow
var myClass: MyClass? {
didSet {
self.commentLabel.text = myClass?.comment
}
}
You have to update the label text itself, right now it's constant with the first load data
My FSCalendar's content shrinks when I switch its scope from week to month if there is a view that's constrained to its bottom anchor.
Here is a quick gif to show what exactly is happening
I have tried everything at this point. Using calendar.setScope() instead of calendar.scope =, constraining attachedToCalendarView.topAnchor to calendar.bottomAnchor calendar.contentView.bottomAnchor, and calendar.daysContainer.bottomAnchor, even turning attachedToCalendarView 's constraints on and off depending on whether it's week scope or scope month.
Not sure what else to try. Here is the code:
import UIKit
import FSCalendar
class TestController : UIViewController, FSCalendarDataSource, FSCalendarDelegate, FSCalendarDelegateAppearance {
fileprivate weak var calendar: FSCalendar!
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
setUp()
}
#objc func switchCalendarScope(){
if self.calendar.scope == FSCalendarScope.month {
self.calendar.scope = FSCalendarScope.week
} else {
self.calendar.scope = FSCalendarScope.month
}
}
func setUp(){
let calendar = FSCalendar()
calendar.dataSource = self
calendar.delegate = self
self.calendar = calendar
self.calendar.scope = .week
self.calendar.locale = Locale(identifier: "en_EN")
self.calendar.calendarHeaderView.calendar.locale = Locale(identifier: "en_EN")
self.calendar.adjustsBoundingRectWhenChangingMonths = true
let testingView = UIView()
testingView.backgroundColor = .red
let attachedToCalendarView = UIView()
attachedToCalendarView.backgroundColor = .blue
view.addSubview(calendar)
view.addSubview(testingView)
view.addSubview(attachedToCalendarView)
self.calendar.translatesAutoresizingMaskIntoConstraints = false
self.calendar.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
self.calendar.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
self.calendar.widthAnchor.constraint(equalToConstant: view.bounds.size.width).isActive = true
self.calendar.heightAnchor.constraint(equalToConstant: 300).isActive = true
testingView.translatesAutoresizingMaskIntoConstraints = false
testingView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
testingView.widthAnchor.constraint(equalToConstant: view.bounds.size.width).isActive = true
testingView.heightAnchor.constraint(equalToConstant: 20).isActive = true
attachedToCalendarView.translatesAutoresizingMaskIntoConstraints = false
attachedToCalendarView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
// Attaching this view's topAnchor to the calendar's bottom anchor
attachedToCalendarView.topAnchor.constraint(equalTo: self.calendar.contentView.bottomAnchor).isActive = true
attachedToCalendarView.widthAnchor.constraint(equalToConstant: view.bounds.size.width).isActive = true
attachedToCalendarView.heightAnchor.constraint(equalToConstant: 20).isActive = true
// Title and button to toggle the calendar scope
self.navigationItem.title = "Test"
self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Toggle", style: .done, target: self, action: #selector(switchCalendarScope))
}
}
Well, I couldn't figure out how to fix the problem itself but I did find a workaround. I placed the calendar inside of an empty container view (just a simple UIView) and attached attachedToCalendarView to the container's bottomAnchor instead of the calendar's itself.
Do note however that using setScope, which animates the transition, still causes the same issue. For it to work you have to set it manually like calendar.scope = x
example:
#objc func switchCalendarScope(){
if self.calendar.scope == FSCalendarScope.month {
// self.calendar.setScope(FSCalendarScope.week, animated: true) // this will cause the calendar to be squished again
self.calendar.scope = .week
movingConstraint.constant = view.safeAreaLayoutGuide.layoutFrame.size.height * -0.20
} else {
// self.calendar.setScope(FSCalendarScope.month, animated: true)
self.calendar.scope = .month
movingConstraint.constant = 0
}
}
I have created a textField in my main viewcontroller and want to pass data to another VC. I have created all of my UI programmatically, with out storyboard.
I tried passing data inside of a action I added to a button by doing the following:
let destination = InsuranceInformationViewController()
destination.customerNameLabel = customerNameTextField.text
But this does not work for me. Any help is appreciated, thanks in advance.
UPDATE
` //CustomerInformationViewController
override func viewDidLoad() {
super.viewDidLoad()
view.addSubView(customernameTextField)
view.addSubView(continueButton)
view.backgroundColor = UIColor.white
}
let customerNameTextField: UITextField = {
let textField = UITextField()
textField.placeHolderText = "First, Last"
textField.borderStyle = UITextBorderStyle.RoundedRect
textField.translatesAutoresizingMaskIntoConstraints = false
return textField
}()
let continueButton: UIButton = {
let button = UIButton(type: .system)
button.backgroundColor = UIColor.blue
button.setTitle("Continue", for: .normal)
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitleColor(.white, for: .normal)
button.addTarget(self, action:#selector(CustomerInformationViewController ().continueOn), for: .touchUpInside)
return button
}()
#objc func continueOn (){
let destination = InsuranceInformationViewController()
destination.customerNameLabel = customerNameTextField.text
self.navigationController?.pushViewController(InsuranceViewController(), animated: true)
}
//InsuranceViewController
override func viewDidLoad() {
super.viewDidLoad()
view.addSubView(customerNameLabel)
view.backgroundColor = UIColor.white
}
let customerNameLabel: UILabel = {
let label = UILabel()
label.text = "Name"
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
`
When you define destination let destination = InsuranceInformationViewController(), you are creating a new instance of InsuranceInformationViewController.
However in doing this, when you are referring to that instance, anything you try to change will not appear to work because it is not the already-instantiated view controller that you see in your view.
Instead you should do something like this:
let destination = storyboard?.instantiateViewController(withIdentifier: "YOUR_STORYBOARD_ID") as! InsuranceInformationViewController
destination.customerNameLabel = customerNameTextField.text
Hope this works
In the previous version of my view controller (based on storyboard) the search bar was working good.
In the 100% programmatically defined current version (I removed all storyboard references) the search bar has disappeared.
I am trying to understand where the problem lies, without success. Any idea?
Here is the faulty code snippet:
let resultSearchController = UISearchController()
override public func viewDidLoad() {
super.viewDidLoad()
// No nav bar
navigationController?.navigationBar.hidden = true
// — Add table view
view.addSubview(tableView)
tableView.delegate = self
tableView.dataSource = self
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.leftAnchor.constraintEqualToAnchor(view.leftAnchor, constant: 0).active = true
tableView.rightAnchor.constraintEqualToAnchor(view.rightAnchor, constant: 0).active = true
tableView.bottomAnchor.constraintEqualToAnchor(view.bottomAnchor, constant: 0).active = true
// Add search bar as a the table header
resultSearchController.dimsBackgroundDuringPresentation = false
resultSearchController.definesPresentationContext = true
resultSearchController.hidesNavigationBarDuringPresentation = false
resultSearchController.searchResultsUpdater = self
resultSearchController.searchBar.sizeToFit()
resultSearchController.searchBar.delegate = self
tableView.tableHeaderView = resultSearchController.searchBar
/// Add refresh control
refreshControl.attributedTitle = NSAttributedString(string: "Pull to refresh")
refreshControl.addTarget(self, action: #selector(BigHeadViewController.refreshTable), forControlEvents: UIControlEvents.ValueChanged)
tableView.addSubview(refreshControl)
}
I think the problem might be the first line. You have to specify the "searchResultsController" as part of the initialisation - even if it's nil (to search in place). Try
let resultSearchController = UISearchController(searchResultsController:nil)