Can't dismiss ModalViewController - swift

I've got a problem by dismissing a ViewController programmatically.
I connected both Controllers and if I press the Button, the Controller will be presented.
Here is my prepareForSegue method:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let VC = VC()
let blurEffect = UIBlurEffect(style: UIBlurEffectStyle.Dark)
let blurredView = UIVisualEffectView(effect: blurEffect)
blurredView.frame = CGRect(x: 0, y: 0, width: self.view.frame.width, height: self.view.frame.height + 64)
VC.view.frame = self.view.bounds
VC.view.backgroundColor = UIColor.clearColor()
VC.view.addSubview(blurredView)
VC.modalPresentationStyle = UIModalPresentationStyle.OverCurrentContext
var masterViewOfVC = (segue.destinationViewController as! SubjectSelectionViewController).masterView
blurredView.addSubview(masterViewOfVC)
masterViewOfVC.setTranslatesAutoresizingMaskIntoConstraints(false)
let views = ["masterView": masterViewOfVC]
let horizontalConstraintsForMasterView = NSLayoutConstraint.constraintsWithVisualFormat("H:|[masterView]|", options: .AlignAllCenterY, metrics: nil, views: views)
blurredView.addConstraints(horizontalConstraintsForMasterView)
let verticalConstraintsForMasterView = NSLayoutConstraint.constraintsWithVisualFormat("V:|[masterView]|", options: .AlignAllCenterX, metrics: nil, views: views)
blurredView.addConstraints(verticalConstraintsForMasterView)
(segue.destinationViewController as! VC).viewDidLoad()
self.presentViewController(VC, animated: true, completion: nil)
}
So and if I try to dismiss it it doesn't do anything. I already checked if the buttons works and it does.
Here is my code from the other controller:
//viewDidLoad
cancelButton.setTitle("Abbrechen", forState: UIControlState.Normal)
cancelButton.tintColor = UIColor.whiteColor()
cancelButton.addTarget(self, action: Selector("buttonAction:"), forControlEvents: UIControlEvents.TouchUpInside)
cancelButton.enabled = true
//my function
func buttonAction(sender:UIButton!) {
VC().dismissViewControllerAnimated(true, completion: nil)
}
UPDATE
I delete the segue in my Storyboard and I created a modal segue in the IBAction of my Button. I also initialise my "dismiss button" in this action.
Here is my code from the parentViewController:
#IBAction func SubjectSelctionButtonPressed(sender: UIBarButtonItem) {
let subjectSelectionViewController = SubjectSelectionViewController()
let blurEffect = UIBlurEffect(style: UIBlurEffectStyle.Dark)
let blurredView = UIVisualEffectView(effect: blurEffect)
blurredView.frame = CGRect(x: 0, y: 0, width: self.view.frame.width, height: self.view.frame.height + 64)
subjectSelectionViewController.view.frame = self.view.bounds
subjectSelectionViewController.view.backgroundColor = UIColor.clearColor()
subjectSelectionViewController.view.addSubview(blurredView)
subjectSelectionViewController.modalPresentationStyle = UIModalPresentationStyle.OverCurrentContext
var masterViewOfSubjectSelection = SubjectSelectionViewController().masterView
var buttonOfSubjectSelection = SubjectSelectionViewController().cancelButton
buttonOfSubjectSelection.setTitle("Abbrechen", forState: UIControlState.Normal)
buttonOfSubjectSelection.tintColor = UIColor.whiteColor()
buttonOfSubjectSelection.addTarget(self, action: Selector("buttonAction:"), forControlEvents: UIControlEvents.TouchUpInside)
blurredView.addSubview(masterViewOfSubjectSelection)
masterViewOfSubjectSelection.addSubview(buttonOfSubjectSelection)
masterViewOfSubjectSelection.setTranslatesAutoresizingMaskIntoConstraints(false)
let views = ["masterView": masterViewOfSubjectSelection]
let horizontalConstraintsForMasterView = NSLayoutConstraint.constraintsWithVisualFormat("H:|[masterView]|", options: .AlignAllCenterY, metrics: nil, views: views)
blurredView.addConstraints(horizontalConstraintsForMasterView)
let verticalConstraintsForMasterView = NSLayoutConstraint.constraintsWithVisualFormat("V:|[masterView]|", options: .AlignAllCenterX, metrics: nil, views: views)
blurredView.addConstraints(verticalConstraintsForMasterView)
self.presentViewController(subjectSelectionViewController, animated: true, completion: nil)
}
and the action from the button looks like this:
func buttonAction(sender:UIButton!) {
dismissViewControllerAnimated(true, completion: nil)
}
ok now, when the controller is presenting there is nothing except my elements which I created in the action. My viewDidLoad() method of my presenting Controller is called but the constraints and the formattings doesn't get called...
Here is my viewDidLoad() of my modalViewController:
titleLabel.setTranslatesAutoresizingMaskIntoConstraints(false)
cancelButton.setTranslatesAutoresizingMaskIntoConstraints(false)
separatorLine.setTranslatesAutoresizingMaskIntoConstraints(false)
subjectsTableView.setTranslatesAutoresizingMaskIntoConstraints(false)
subjectsTableView.delegate = self
titleLabel.text = "Unterrichtsfächer"
titleLabel.textColor = UIColor.whiteColor()
titleLabel.font = UIFont(name: "HelveticaNeue-Medium", size: 17)
separatorLine.backgroundColor = UIColor.whiteColor()
subjectsTableView.backgroundColor = UIColor.clearColor()
masterView.addSubview(titleLabel)
masterView.addSubview(cancelButton)
masterView.addSubview(separatorLine)
masterView.addSubview(subjectsTableView)
let views = ["line": separatorLine, "title": titleLabel, "button": cancelButton, "table": subjectsTableView]
let verticalConstraintsForElements = NSLayoutConstraint.constraintsWithVisualFormat("V:|-64-[line(2)][table]|", options: .AlignAllCenterX, metrics: nil, views: views)
masterView.addConstraints(verticalConstraintsForElements)
let horizontalConstraintsForSeparatorLine = NSLayoutConstraint.constraintsWithVisualFormat("H:|[line]|", options: .AlignAllCenterY, metrics: nil, views: views)
masterView.addConstraints(horizontalConstraintsForSeparatorLine)
let horizontalConstraintsForTableView = NSLayoutConstraint.constraintsWithVisualFormat("H:|[table]|", options: .AlignAllCenterY, metrics: nil, views: views)
masterView.addConstraints(horizontalConstraintsForTableView)
let verticalConstraintsForTitleLabel = NSLayoutConstraint.constraintsWithVisualFormat("V:[title(21)]-11-[line]", options: .AlignAllCenterX, metrics: nil, views: views)
masterView.addConstraints(verticalConstraintsForTitleLabel)
let horizontalAlignmentForTitleLabel = NSLayoutConstraint(item: titleLabel, attribute: NSLayoutAttribute.CenterX, relatedBy: .Equal, toItem: masterView, attribute: .CenterX, multiplier: 1, constant: -18)
masterView.addConstraint(horizontalAlignmentForTitleLabel)
let widthForTitleLabel = NSLayoutConstraint(item: titleLabel, attribute: .Width, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: 137)
masterView.addConstraint(widthForTitleLabel)
let verticalConstraintsForCancelButton = NSLayoutConstraint.constraintsWithVisualFormat("V:[button(30)]-6-[line]", options: .AlignAllCenterX, metrics: nil, views: views)
masterView.addConstraints(verticalConstraintsForCancelButton)
let horizontalConstraintsForCancelButton = NSLayoutConstraint.constraintsWithVisualFormat("H:[button(90)]-7-|", options: .AlignAllCenterY, metrics: nil, views: views)
masterView.addConstraints(horizontalConstraintsForCancelButton)
Can anybody help me?
Thank you very much!!!

Your problem is that you are not pushing the other view to dismiss it, you are just adding the new view controller on top of the other view
You need to substitute this line
blurredView.addSubview(masterViewOfVC)
For
presentViewController(masterViewOfVC, animated: true, completion: nil)
Now when you call dismiss view controller all should just work

I haven't really checked all your code but to fix it quickly, you can make the instance of VC a class property.
So it'll look something like this:
class MainViewController: UIViewController {
var VC = UIViewController() // I would use an optional here but I've been down voted for suggesting people to use optionals.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
VC = VC()
// Your code
}
func buttonAction(sender:UIButton!) {
VC.dismissViewControllerAnimated(true, completion: nil)
}
}
Here I'm assuming you're using some kind of master - detail view so buttonAction is still accessible when you segue to VC.
If buttonAction is a method in VC itself, then just call dissmissViewControllerAnimated() on self with:
func buttonAction(sender:UIButton!) {
dismissViewControllerAnimated(true, completion: nil)
}
Either way, you'll have to call dissmissViewControllerAnimated() on the instance of VC. With VC() you'll just create a new instance which has "nothing" to do with the one that already exists.
Update:
If you've created SubjectSelectionViewController() in your storyboard, then you probably would want to instantiate it with:
let storyboard = UIStoryboard(name: "Main", bundle: NSBundle.mainBundle())
let subjectSelectionViewController = storyboard.instantiateViewControllerWithIdentifier("YourIdentifier") as! SubjectSelectionViewController

Related

UISegmentedControl: segment overlapping when pass data back

thanks Duncan C. I edit my question:
i have a ViewControllerA with a 3 segments SegmentedControl, on ViewControllerB you can delete one segment and go back to VCA.
when I do so, I have, in VCA, 2 segments (the new segments after deletion) over the 3 segments (the 3 segments initially loaded).
VCA before change
VCA after change
I understand that it has to do with the view cycle, the initial ViewDidLoad persists, but even passing through viewWillAppear I still have this result.
this project is not a real project, just a test to solve the problem I have on my initial project.
my code for VCA:
class VCA: UIViewController, Info {
// array which contains the name for the segmentedControl
var array = ["Peter", "Bob", "Jim"]
var segmentedControl: UISegmentedControl!
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
setView(array: array)
}
override func viewDidLoad() {
super.viewDidLoad()
}
// delegate protocol: receive data from VCB
func passData(array: [String]) {
self.array = array
}
func setView(array: [String]) {
segmentedControl = UISegmentedControl(items: array)
segmentedControl.selectedSegmentIndex = 0
segmentedControl.translatesAutoresizingMaskIntoConstraints = false
let button = UIButton(type: .system)
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitle("OK", for: .normal)
button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
let stackView = UIStackView()
stackView.axis = .vertical
stackView.distribution = .fillEqually
stackView.spacing = 50
stackView.addArrangedSubview(segmentedControl)
stackView.addArrangedSubview(button)
view.addSubview(stackView)
stackView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
stackView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20),
stackView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -20),
stackView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 20),
stackView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -20)])
}
#objc func buttonTapped() {
let destinationVC = VCB()
destinationVC.array = array
destinationVC.infoDelegate = self
destinationVC.modalPresentationStyle = .fullScreen
present(destinationVC, animated: true, completion: nil)
}
}
my code for VCB:
class VCB: UIViewController {
// array which contains the name of the segments
var array = [String]()
// delegate to send data back
var infoDelegate: Info!
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
override func viewDidLoad() {
super.viewDidLoad()
setView(array: array)
}
func setLabel(array: [String]) -> String {
var labelText = "the array contains"
for name in array {
labelText += " \(name)"
}
return labelText
}
func setView(array: [String]) {
let label = UILabel(frame: CGRect(x: 0, y: 0, width: 200, height: 50))
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
label.text = setLabel(array: array)
view.addSubview(label)
view.backgroundColor = .cyan
NSLayoutConstraint.activate([
label.centerYAnchor.constraint(equalTo: view.centerYAnchor),
label.centerXAnchor.constraint(equalTo: view.centerXAnchor)])
let button = UIButton(type: .system)
button.setTitle("Delete the first name in the array", for: .normal)
button.addTarget(self, action: #selector(backTapped), for: .touchUpInside)
button.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(button)
NSLayoutConstraint.activate([
button.topAnchor.constraint(equalTo: label.bottomAnchor, constant: 40),
button.centerXAnchor.constraint(equalTo: label.centerXAnchor)])
}
#objc func backTapped() {
array.remove(at: 0)
infoDelegate.passData(array: array)
dismiss(animated: true, completion: nil)
}
}
with a protocol to send data back to VCB:
protocol Info {
func passData(array: [String])
}
any ideas/help?
thanks a lot
I gather VCA is view controller A and VCB is view controller B. You're saying that from VCB you delete one segment of VCA's segmented controller?
You should not mess with another view controller's views.
You will need to post your code and explain how the user navigates between view controllers in order for us to be able to help you.
(And by the way, those huge segmented controls look absurd. You should leave them at the standard height.)
I think I have found a solution. I remove all segments of the UISegment before repopulating it with the new data received from ViewController B.
func passData(array: [String]) {
self.array =array
segmentedControl.removeAllSegments()
segmentedControl = UISegmentedControl(items: array)
}
If the solution seems to work, it is not very elegant.

userDefualt not saving switch when class is segued

My swift code below which is all code no storyboard. Tries to save a switch's state using user defaults. So when the user segues to twoViewController and then segues backs to ViewController. The users switch setting is not being saved. There are no errors presented and I don't know what is going on.
import UIKit
class ViewController: UIViewController {
var nxtBTn = UIButton()
var mySwitch = UISwitch()
let userDefaults = UserDefaults.standard
var firstTimeAppLaunch: Bool {
get {
// Will return false when the key is not set.
return userDefaults.bool(forKey: "firstTimeAppLaunch")
}
set {}
}
#objc func switchAction(_ sender: UISwitch) {
userDefaults.set(sender.isOn, forKey: "mySwitchValue")
}
#objc func press() {
let segue = twoViewController()
segue.modalPresentationStyle = .fullScreen // actually .fullScreen would be better
self.present(segue, animated: true)
}
override func viewDidLoad() {
super.viewDidLoad()
[nxtBTn,mySwitch].forEach {
$0.translatesAutoresizingMaskIntoConstraints = false
view.addSubview($0)
$0.backgroundColor = .green
}
if !firstTimeAppLaunch {
// This will only be trigger first time the application is launched.
userDefaults.set(true, forKey: "firstTimeAppLaunch")
userDefaults.set(true, forKey: "mySwitchValue")
}
NSLayoutConstraint.activate([
nxtBTn.leadingAnchor.constraint(equalTo: view.leadingAnchor),
nxtBTn.topAnchor.constraint(equalTo: view.topAnchor),
nxtBTn.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 7/7, constant: 0),
nxtBTn.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.4, constant: 0),
mySwitch.leadingAnchor.constraint(equalTo: view.leadingAnchor),
mySwitch.bottomAnchor.constraint(equalTo: view.bottomAnchor),
mySwitch.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 7/7, constant: 0),
mySwitch.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.4, constant: 0),
])
mySwitch.addTarget(self, action: #selector(switchAction(_:)), for: .touchDown)
nxtBTn.addTarget(self, action: #selector(press), for: .touchDown)
view.backgroundColor = .white
}
override func viewDidAppear(_ animated: Bool) {
mySwitch.isOn = userDefaults.bool(forKey: "mySwitchValue")
}
}
class twoViewController : UIViewController{
var backBtn = UIButton()
override func viewDidLoad() {
super.viewDidLoad()
backBtn.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(backBtn)
backBtn.backgroundColor = .systemGreen
NSLayoutConstraint.activate([
backBtn.leadingAnchor.constraint(equalTo: view.leadingAnchor),
backBtn.topAnchor.constraint(equalTo: view.topAnchor),
backBtn.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 7/7, constant: 0),
backBtn.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.4, constant: 0),
])
backBtn.addTarget(self, action: #selector(oneVC), for: .touchDown)
}
#objc func oneVC(){
let segue = ViewController()
segue.modalPresentationStyle = .fullScreen // actually .fullScreen would be better
self.present(segue, animated: true)
}
}
Everywhere you are saying for: .touchDown it is wrong. Change all of them to for: .primaryActionTriggered and things will improve.

Manually connect barbutton outlet to action through code

I have managed to code four UIBarButton items that appear when I run my app. However, when I click on one of the buttons (to go to a new screen), the app crashes with a SIGBART error. I know from previous experiences that this may be caused by not having outlets connect. But because I have coded the buttons to appear in my file, I am unsure of how to connect the outlet and actions through code. Any guidance would be much appreciated!
import UIKit
class navigationToolBar: UINavigationController, UIToolbarDelegate {
#IBOutlet weak var searchBarButton1: UIBarButtonItem!
override func viewDidLoad() {
super.viewDidLoad()
print(UIApplication.shared.statusBarFrame.height)//44 for iPhone x, 20 for other iPhones
navigationController?.navigationBar.barTintColor = .blue
self.toolbar.barTintColor = UIColor.black
var searchicon = UIImage(named: "searchicon")!
var protocolsicon = UIImage(named: "protocolsicon")!
var homeicon = UIImage(named: "homeicon")!
var toolsicon = UIImage(named: "toolsicon")!
let search1 = self.storyboard?.instantiateViewController(withIdentifier: "searchn") as! UINavigationController
let protocols1 = self.storyboard?.instantiateViewController(withIdentifier: "protocolsn") as! UINavigationController
let home1 = self.storyboard?.instantiateViewController(withIdentifier: "homen") as! UINavigationController
// let tools1 = self.storyboard?.instantiateInitialViewController(withIdentifier: "initial") as! imageViewController
// let info1 = self.storyboard?.instantiateViewController(withIdentifier: "Information") as! imageViewController
let toolBar = UIToolbar()
var items = [UIBarButtonItem]()
var searchBarButton1 = UIBarButtonItem(image: searchicon, style: .plain, target: self, action: Selector(("opensearchbar1")))
items.append(
UIBarButtonItem(image: searchicon, style: .plain, target: self, action: Selector(("opensearchbar1")))
)
items.append(
UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
)
items.append(
UIBarButtonItem(image: protocolsicon, style: .plain, target: self, action: Selector(("opensearchbar")))
)
items.append(
UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
)
items.append(
UIBarButtonItem(image: homeicon, style: .plain, target: self, action: Selector(("onClickBarButton:")))
)
items.append(
UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
)
items.append(
UIBarButtonItem(image: toolsicon, style: .plain, target: self, action: Selector(("onClickBarButton:")))
)
items.append(
UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
)
items.append(
UIBarButtonItem(image: toolsicon, style: .plain, target: self, action: Selector(("onClickBarButton:")))
)
func opensearchbar(_ sender: UIBarButtonItem) {
switch sender.tag {
case 1:
print("1")
break;
case 2:
print("error")
break;
case 3:
self.navigationController?.pushViewController(protocols1, animated: true)
break;
case 4:
print("error")
break;
case 5:
self.navigationController?.pushViewController(home1, animated: true)
break;
case 6:
print("error")
break;
case 7:
self.navigationController?.pushViewController(home1, animated: true)
break;
case 8:
print("error")
break;
// case 9:
// self.navigationController?.pushViewController(info1, animated: true)
// break;
default:
print("ERROR!!")
}
}
toolBar.setItems(items, animated: true)
toolBar.tintColor = .blue
toolbar.barStyle = UIBarStyle.default
toolBar.isTranslucent = true
view.addSubview(toolBar)
toolBar.translatesAutoresizingMaskIntoConstraints = false
if #available(iOS 11.0, *) {
let guide = self.view.safeAreaLayoutGuide
toolBar.trailingAnchor.constraint(equalTo: guide.trailingAnchor).isActive = true
toolBar.leadingAnchor.constraint(equalTo: guide.leadingAnchor).isActive = true
toolBar.bottomAnchor.constraint(equalTo: guide.bottomAnchor).isActive = true
toolBar.heightAnchor.constraint(equalToConstant: 44).isActive = true
}
else {
NSLayoutConstraint(item: toolBar, attribute: .bottom, relatedBy: .equal, toItem: view, attribute: .bottom, multiplier: 1.0, constant: 0).isActive = true
NSLayoutConstraint(item: toolBar, attribute: .leading, relatedBy: .equal, toItem: view, attribute: .leading, multiplier: 1.0, constant: 0).isActive = true
NSLayoutConstraint(item: toolBar, attribute: .trailing, relatedBy: .equal, toItem: view, attribute: .trailing, multiplier: 1.0, constant: 0).isActive = true
toolBar.heightAnchor.constraint(equalToConstant: 44).isActive = true
}
}
func opensearchbar1 (_ sender: UIBarButtonItem) {
let search1 = self.storyboard?.instantiateViewController(withIdentifier: "searchn") as! ViewController
self.navigationController?.pushViewController(search1, animated: true)
}
}

Views centered in parent below each other

I'm trying to make this view with the Layout extension, I tried a little bit, but can't figure it out.
This is my code so far:
import UIKit
import Material
class ViewController: UIViewController {
private var nameField: TextField!
private var emailField: ErrorTextField!
private var passwordField: TextField!
private let constant: CGFloat = 32
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = Color.indigo.base
prepareNameField()
preparePasswordField()
prepareResignResponderButton()
}
/// Prepares the resign responder button.
private func prepareResignResponderButton() {
let btn = FlatButton(title: "Login", titleColor: Color.white)
btn.addTarget(self, action: #selector(handleResignResponderButton(button:)), for: .touchUpInside)
view.layout(btn).width(100).height(constant).right(0).top(8 * constant).horizontally(left: constant, right: constant);
}
/// Handle the resign responder button.
#objc
internal func handleResignResponderButton(button: UIButton) {
nameField?.resignFirstResponder()
passwordField?.resignFirstResponder()
}
private func prepareNameField() {
nameField = TextField()
nameField.placeholderNormalColor = Color.indigo.lighten4
nameField.placeholderActiveColor = Color.white
nameField.dividerNormalColor = Color.indigo.lighten4
nameField.dividerActiveColor = Color.white
nameField.isClearIconButtonEnabled = true
nameField.textColor = Color.white
nameField.placeholder = "Username"
view.layout(nameField).top(4 * constant).horizontally(left: constant, right: constant)
}
private func preparePasswordField() {
passwordField = TextField()
passwordField.placeholderNormalColor = Color.indigo.lighten4
passwordField.placeholderActiveColor = Color.white
passwordField.dividerNormalColor = Color.indigo.lighten4
passwordField.dividerActiveColor = Color.white
passwordField.isClearIconButtonEnabled = true
passwordField.textColor = Color.white
passwordField.placeholder = "Password"
passwordField.clearButtonMode = .whileEditing
passwordField.isVisibilityIconButtonEnabled = true
// Setting the visibilityIconButton color.
passwordField.visibilityIconButton?.tintColor = Color.white.withAlphaComponent(passwordField.isSecureTextEntry ? 0.38 : 0.54)
view.layout(passwordField).top(6 * constant).horizontally(left: constant, right: constant)
}
}
I'm new to swift so if someone can explain me how to accomplish it that would be great.
Here is an example with TextFields.
import UIKit
import Material
class ViewController: UIViewController {
fileprivate var emailField: ErrorTextField!
fileprivate var passwordField: TextField!
/// A constant to layout the textFields.
fileprivate let constant: CGFloat = 32
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = Color.grey.lighten5
preparePasswordField()
prepareEmailField()
prepareResignResponderButton()
}
/// Prepares the resign responder button.
fileprivate func prepareResignResponderButton() {
let btn = RaisedButton(title: "Resign", titleColor: Color.blue.base)
btn.addTarget(self, action: #selector(handleResignResponderButton(button:)), for: .touchUpInside)
view.layout(btn).width(100).height(constant).centerVertically(offset: emailField.height / 2 + 60).right(20)
}
/// Handle the resign responder button.
#objc
internal func handleResignResponderButton(button: UIButton) {
emailField?.resignFirstResponder()
passwordField?.resignFirstResponder()
}
}
extension ViewController {
fileprivate func prepareEmailField() {
emailField = ErrorTextField()
emailField.placeholder = "Email"
emailField.detail = "Error, incorrect email"
emailField.isClearIconButtonEnabled = true
emailField.delegate = self
view.layout(emailField).center(offsetY: -passwordField.height - 60).left(20).right(20)
}
fileprivate func preparePasswordField() {
passwordField = TextField()
passwordField.placeholder = "Password"
passwordField.detail = "At least 8 characters"
passwordField.clearButtonMode = .whileEditing
passwordField.isVisibilityIconButtonEnabled = true
// Setting the visibilityIconButton color.
passwordField.visibilityIconButton?.tintColor = Color.green.base.withAlphaComponent(passwordField.isSecureTextEntry ? 0.38 : 0.54)
view.layout(passwordField).center().left(20).right(20)
}
}
extension UIViewController: TextFieldDelegate {
public func textFieldDidEndEditing(_ textField: UITextField) {
(textField as? ErrorTextField)?.isErrorRevealed = false
}
public func textFieldShouldClear(_ textField: UITextField) -> Bool {
(textField as? ErrorTextField)?.isErrorRevealed = false
return true
}
public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
(textField as? ErrorTextField)?.isErrorRevealed = false
return true
}
}
As you can see below, with the visual formatting language, you're basically painting with code.
import UIKit
class ViewController: UIViewController {
var view1:UILabel!
var view2:UILabel!
var view3:UILabel!
override func viewDidLoad() {
super.viewDidLoad()
initViews();
initLayouts();
}
func initViews() {
view.backgroundColor = UIColor.white
view1 = createLabel(title: "view1")
view2 = createLabel(title: "view2")
view3 = createLabel(title: "view3")
view.addSubview(view1)
view.addSubview(view2)
view.addSubview(view3)
}
func initLayouts() {
for view in view.subviews {
view.translatesAutoresizingMaskIntoConstraints = false
}
var views = [String: AnyObject]()
views["view1"] = view1
views["view2"] = view2
views["view3"] = view3
// ---------------------------------------------------------
// Setup horizontal constraints for all three views
// ---------------------------------------------------------
view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-20-[view1]-20-|",
options: NSLayoutFormatOptions(rawValue: 0),
metrics: nil,
views: views))
view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-20-[view2(==view1)]-20-|",
options: NSLayoutFormatOptions(rawValue: 0),
metrics: nil,
views: views))
view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:[view3]-20-|",
options: NSLayoutFormatOptions(rawValue: 0),
metrics: nil,
views: views))
// special layout condition to make third view half the width of other views
view.addConstraint(NSLayoutConstraint(item: view3,
attribute: NSLayoutAttribute.width,
relatedBy: NSLayoutRelation.equal,
toItem: view1,
attribute: NSLayoutAttribute.width,
multiplier: 0.5,
constant: 0.0))
// ---------------------------------------------------------
// Setup vertical constraints for all three views
// ---------------------------------------------------------
view.addConstraints(
NSLayoutConstraint.constraints(withVisualFormat: "V:|-20-[view1]-20-[view2]-20-[view3]",
options: NSLayoutFormatOptions(rawValue: 0),
metrics: nil,
views: views))
// make all three views equal height
view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:[view1(44)]",
options: NSLayoutFormatOptions(rawValue: 0),
metrics: nil,
views: views))
view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:[view2(==view1)]",
options: NSLayoutFormatOptions(rawValue: 0),
metrics: nil,
views: views))
view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:[view3(==view1)]",
options: NSLayoutFormatOptions(rawValue: 0),
metrics: nil,
views: views))
}
// ------------------------------------------------
// Helper function
// ------------------------------------------------
func createLabel(title:String) -> UILabel {
let textLabel = UILabel()
textLabel.text = title
textLabel.backgroundColor = UIColor.red
textLabel.textColor = UIColor.black
textLabel.textAlignment = NSTextAlignment.center
textLabel.layer.borderColor = UIColor.black.cgColor
textLabel.layer.borderWidth = 2.0
return textLabel
}
}
You should see something like this:

navigation bar button text disappears

I have 2 nav bar buttons at the top of the screen, one with an image an another with text like so:
When I present a new view controller with a new nav controller/root controller and then dismiss it, going back to the same view controller, the text disappears like so:
Here's the code for the view controller
override func viewDidLoad() {
setupNavBarButtons()
}
func setupNavBarButtons() {
let searchImage = UIImage(named: "search_icon")?.imageWithRenderingMode(.AlwaysOriginal)
let searchBarButtonItem = UIBarButtonItem(image: searchImage, style: .Plain, target: self, action: #selector(handleSearch))
navigationItem.rightBarButtonItem = searchBarButtonItem
let filterBarButtonItem = UIBarButtonItem(title: "Filter", style: .Plain , target: self, action: #selector(displayFilter))
navigationItem.leftBarButtonItem = filterBarButtonItem
}
func presentAccountController() {
let accountController = AccountController()
accountController.listController = self
let vc = UINavigationController(rootViewController: accountController
presentViewController(vc, animated: true, completion: nil)
}
The controller I'm dismissing to return to the original view controller:
class AccountController: UIViewController, UITableViewDelegate, UITableViewDataSource, MFMailComposeViewControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
setupViews()
view.backgroundColor = UIColor.rgb(245, green: 245, blue: 245)
let closeButtonItem = UIBarButtonItem(title: "Close", style: .Plain, target: self, action: #selector(dismissController))
navigationItem.leftBarButtonItem = closeButtonItem
}
func dismissController() {
self.dismissViewControllerAnimated(true, completion: nil)
}
I've tried calling it in viewWillLoad, viewDidLoad, viewDidAppear. Still no change and the bug still occurs.
You can do this and create a frame with a wide width when you create the button
lazy var filterButton: UIButton = {
let button = UIButton(type: .system)
button.frame = CGRect(x: 0, y: 0, width: 60, height: 30) // Set the width here to make it wider
button.setTitle("Filter", for: .normal)
button.titleLabel?.font = UIFont.systemFont(ofSize: 18)
button.setTitleColor(UIColor.white, for: .normal)
button.addTarget(self, action: #selector(filterButtonTapped), for: .touchUpInside)
return button
}()
var leftBarButtonItem: UIBarButtonItem?
override func viewDidLoad() {
super.viewDidLoad()
leftBarButtonItem = UIBarButtonItem(customView: filterButton)
navigationItem.leftBarButtonItem = leftBarButtonItem
}
#objc func filterButtonTapped() {
print("Filter button tapped")
}