I have multiple subviews in my main view controller, I am using a delete button to remove one subview at a time. I am trying to allow the user to bring back the view that was deleted, but the view is not coming back. Any thoughts? In Swift.
#IBOutlet var tornView: UIView!
var deleted = 1
// Delete Button
#IBAction func deleteViewButton(sender: AnyObject) {
if deleted == 1 {
tornView.removeFromSuperview()
deleted = 2
}
}
// Brings View to Screen
#IBAction func showTornAnnotation(sender: AnyObject) {
if toggleState == 1 {
firstSlider.hidden = false
tornView.hidden = false
toggleState = 2
if deleted == 2 {
view.addSubview(tornView)
}
}
else {
firstSlider.hidden = true
tornView.hidden = true
toggleState = 1
}
}
If you want IBOutlet to be removed from the superView and get it added back then you should always use strong references to your IBOutlet. Being said you should also keep the position of the removed view so that you can use it when you are ready to add it back.
Edit: Sample code
#IBOutlet var customView: UIView!
var customViewFrame: CGRect?
override func viewDidLoad() {
super.viewDidLoad()
customView.backgroundColor = UIColor.blueColor()
}
#IBAction func remove(sender: AnyObject) {
customViewFrame = customView.frame
customView.removeFromSuperview()
}
#IBAction func add(sender: AnyObject) {
if let rect = customViewFrame {
customView = UIView.init(frame: rect)
customView.backgroundColor = UIColor.blueColor()
view.addSubview(customView)
view.bringSubviewToFront(customView)
}
}
Related
I'm creating a simple log in VC. let's ignore the input validation for both username & password. I just want to enable the UIButton when both username's and password's UITextField is not empty. And whenever any one of them becomes empty, I want the button to be disabled.
#IBAction func typingUserName(_ sender: Any) {
usernameTxtfield.layer.shadowPath = UIBezierPath(rect: usernameTxtfield.bounds).cgPath
usernameTxtfield.layer.shadowRadius = 2
usernameTxtfield.layer.shadowOffset = .zero
usernameTxtfield.layer.shadowOpacity = 0.5
signInIcon.isEnabled = false
}
#IBAction func typingPassword(_ sender: Any) {
passwordTxtfield.layer.shadowPath = UIBezierPath(rect: passwordTxtfield.bounds).cgPath
passwordTxtfield.layer.shadowRadius = 2
passwordTxtfield.layer.shadowOffset = .zero
passwordTxtfield.layer.shadowOpacity = 0.5
signInIcon.isEnabled = false
}
#IBAction func usernameTxtFieldEditingChnged(_ sender: Any) {
usernameTxtfield.layer.shadowRadius = 0
usernameTxtfield.layer.shadowOffset = .zero
usernameTxtfield.layer.shadowOpacity = 0
}
#IBAction func passwordEditingChaned(_ sender: Any) {
passwordTxtfield.layer.shadowRadius = 0
passwordTxtfield.layer.shadowOffset = .zero
passwordTxtfield.layer.shadowOpacity = 0
signInIcon.isEnabled = true
}
#IBAction func signInClicked(_ sender: Any) {
performSegue(withIdentifier: "welcomeVC", sender: signInIcon)
}
As you can see, I'm enabling the button only after the password textfield EditingChanged has been triggered.
You can observe event .editingChanged.
passwordTxtfield.addTarget(self, action: #selector(passwordEditingChaned), for: .editingChanged)
usernameTxtfield.addTarget(self, action: #selector(usernameTxtFieldEditingChnged), for: .editingChanged)
And then add check in both methods:
signInIcon.isEnabled = passwordTxtfield.text?.isEmpty == false && usernameTxtfield.text?.isEmpty == false
You have to implement something like this:
if usernameTxtfield.text != "" && passwordTxtfield.text != "" {
signInIcon.isEnabled = true
} else {
signInIcon.isEnabled = false
}
You add this piece of code in the action of each UITextField and your are good to go
Connect outlets of textFields and Button to ViewController Class.
#IBOutlet private weak var usernameTxtfield: UITextField!
#IBOutlet private weak var passwordTxtfield: UITextField!
#IBOutlet private weak var signInButton: UIButton!
then write your setup function
private func setupTextFields() {
// write your TextFields custom setup ...
// also add delegate lines
usernameTxtfield.delegate = self
passwordTxtfield.delegate = self
}
your setupTextFields function to viewDidLoad of VC and also set your button isEnable = false
override func viewDidLoad() {
super.viewDidLoad()
setupTextFileds()
setSignInButton(isEnable: false)
}
and also write func to get textFields.
private func getTextFields() -> [UITextField] {
return [usernameTxtfield, passwordTxtfield]
}
also we need to check if these are valid.
private func isInputsValid() -> Bool {
var isValid: Bool = true
let inputs: [UITextField] = getTextFields()
if let input = inputs.first(where: { $0.text?.count == 0 }) {
debugPrint("\(input) is not valid.")
isValid = false
}
return isValid
}
also add func to set Button
private func setSignInButton(isEnable: Bool) {
signInButton.isEnabled = isEnable
}
then write TextField delegate func to understand inputs are changing and change condition of button.
extension ViewController: UITextFieldDelegate {
func textFieldDidChangeSelection(_ textField: UITextField) {
setSignInButton(isEnable: isInputsValid())
}
}
I hope it was helpful :)
I have created one popView with textfield and button in ViewController. if i click button then popView is appearing, and i am able to enter text in textfield and submit is working, and if i tap anywhere in view also i am able to remove popView, but here i want if i tap on anywhere in popView i don't want to dismiss popView, Please help me in the code.
here is my code:
import UIKit
class PopUPViewController: UIViewController {
#IBOutlet weak var popView: UIView!
#IBOutlet weak var inputField: UITextField!
#IBOutlet weak var textLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
popView.isHidden = true
// Do any additional setup after loading the view.
}
#IBAction func butnAct(_ sender: Any) {
view?.backgroundColor = UIColor(white: 1, alpha: 0.9)
popView.isHidden = false
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(PopUPViewController.dismissView))
view.addGestureRecognizer(tap)
}
#objc func dismissView() {
self.popView.isHidden = true
view?.backgroundColor = .white
}
#IBAction func sendButton(_ sender: Any) {
self.textLabel.text = inputField.text
}
}
In my code if i tap anywhere in the view popView is removing even if i tap on popView also its removing, i don't need that, if i tap on popView then popView need not to be remove.
Please help me in the code
You can override the touchesBegan method which is triggered when a new touch is detected in a view or window. By using this method you can check a specific view is touched or not.
Try like this
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first
if touch?.view != self.popView {
dismissView()
}
}
func dismissView() {
self.popView.isHidden = true
view?.backgroundColor = .white
}
It's not the way I would have architected this, but to get around the problem you face you need to adapt your dismissView method so that it only dismisses the view if the tap is outside the popView.
To do this modify your selector to include the sender (the UITapGestureRecogniser )as a parameter:
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(PopUPViewController.dismissView(_:)))
and then in your function accept that parameter and test whether the tap is inside your view, and if so don't dismiss the view:
#objc func dismissView(_ sender: UITapGestureRegognizer) {
let tapPoint = sender.location(in: self.popView)
if self.popView.point(inside: tapPoint, with: nil)) == false {
self.popView.isHidden = true
view?.backgroundColor = .white
}
}
Your Popup view is inside the parent view of viewcontroller that's why on tap of popview also your popview is getting hidden.
So to avoid just add a view in background and name it bgView or anything what you want and replace it with view. And it will work fine .
Code:
#IBOutlet weak var bgView: UIView!//Add this new outlet
#IBOutlet weak var popView: UIView!
#IBOutlet weak var inputField: UITextField!
#IBOutlet weak var textLabel: UILabel!
#IBOutlet weak var submitButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
popView.isHidden = true
}
#IBAction func butnAct(_ sender: Any) {
bgView.backgroundColor = UIColor(white: 1, alpha: 0.9)//change view to bgView[![enter image description here][1]][1]
popView.isHidden = false
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(ViewController.dismissView))
bgView.addGestureRecognizer(tap)//change view to bgView
}
#objc func dismissView() {
self.popView.isHidden = true
bgView.backgroundColor = .white//change view to bgView
}
#IBAction func sendButton(_ sender: Any) {
self.textLabel.text = inputField.text
}
I want to update the layout of a popover, I've already tried using the setNeedsLayout() and layoutIfNeeded() methods but couldn't find a solution.
I have a simple screen with 4 buttons that call a popover, within the popover there are 2 extra buttons:
Button -> hides button3, and moves button1 and button2 to the left.
reposition -> changes the sourceview of the popover from the button1/2/3/4 (sender) to button4.
Picture of the layout
The problem I have is that I'm not able to update/refresh the screen, and the popover keeps pointing to the former source view position.
Picture of the popover
Somehow I need a method that updates my view and which is stronger than layoutIfNeeded() because if the screen is changed from portrait/landscape the popover changes automatically. Here I changed portrait/landscape/portrait
GitHub of the project. GitHub
The code used is the following:
class ViewController: UIViewController, ButtonDidDisappearDelegate {
#IBOutlet weak var button1: UIButton!
#IBOutlet weak var button2: UIButton!
#IBOutlet weak var button3: UIButton!
#IBOutlet weak var button4: UIButton!
#IBOutlet weak var constrain2: NSLayoutConstraint!
var popVC: PopVC?
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func buttonPopover(_ sender: UIButton) {
configureAndPresentPopover(from: sender)
}
func buttonDisappear() {
self.constrain2.constant = 100
self.button3.isHidden = !self.button3.isHidden
if !self.button3.isHidden {
self.constrain2.constant = 10
}
}
func repositionPopover() {
DispatchQueue.main.async {
if let popVC = self.popVC {
self.self.popoverPresentationController(popVC.popoverPresentationController!, willRepositionPopoverTo: self.button4.bounds, in: self.button4)
}
self.view.setNeedsLayout()
self.view.layoutIfNeeded()
}
}
}
extension ViewController: UIPopoverPresentationControllerDelegate {
func configureAndPresentPopover(from sender: UIButton) {
popVC = storyboard?.instantiateViewController(withIdentifier: "popVC") as? PopVC
guard let popVC = popVC else {
return
}
popVC.popOverDelegate = self
popVC.modalPresentationStyle = .popover
let popOverVC = popVC.popoverPresentationController
popOverVC?.delegate = self
popOverVC?.sourceView = sender
popOverVC?.sourceRect = sender.bounds
popVC.preferredContentSize = CGSize(width: 300, height: 60)
popOverVC?.permittedArrowDirections = .down
self.present(popVC, animated: true)
}
func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle {
return .none
}
func popoverPresentationController(_ popoverPresentationController: UIPopoverPresentationController,
willRepositionPopoverTo rect: CGRect,
in view: UIView) {
popoverPresentationController.sourceView = view
popoverPresentationController.sourceRect = rect
}
}
and the popover controller:
protocol ButtonDidDisappearDelegate: class {
func configureAndPresentPopover(from sender: UIButton)
func buttonDisappear()
func repositionPopover()
}
class PopVC: UIViewController {
weak var popOverDelegate: ButtonDidDisappearDelegate?
override func viewDidLoad() {
super.viewDidLoad()
}
#IBOutlet weak var popoverButton: UIButton!
#IBAction func popoverAction(_ sender: Any) {
popOverDelegate?.buttonDisappear()
DispatchQueue.main.async {
self.view.setNeedsLayout()
self.view.layoutIfNeeded()
}
}
#IBAction func reposition(_ sender: Any) {
popOverDelegate?.repositionPopover()
}
}
You need to specify the new position of the arrow and ask for the layout of the presentation controller's view, not only the presented view.
Try :
func repositionPopover() {
let popOverController = presentedViewController?.presentationController as? UIPopoverPresentationController
popOverController?.sourceView = button4
popOverController?.containerView?.setNeedsLayout()
popOverController?.containerView?.layoutIfNeeded()
}
You can try setting the sourceview and sourcerect once again. call this function when the sourcview bounds are changed.
I have been looking around the web and I am unable to find a solution for my problem. I want my "START" button text to change to "RESTART" once the game has ended. If any additional information is needed please let me know. Thanks, all help is appreciated. Below is my code.
class ViewController: UIViewController {
var gameOver = false
var stopFuncs = false
var gameTimer = Timer()
var counter = 10
var selected = "NONE"
var score = 0
var colour = "NONE"
#IBOutlet weak var colourLabel: UILabel!
let colourProvider = ColourProvider()
#IBOutlet weak var scoreLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
colour = colourProvider.randomColour()
print(colour)
colourLabel.text = colour
// 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.
}
#IBOutlet weak var counterLabel: UILabel!
func updateCounter() {
if counter >= 0 {
counterLabel.text = String(counter)
counter -= 1
}
}
#IBAction func startButton() {
gameOver = false
counter = 10
stopFuncs = false
var _ = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(updateCounter), userInfo: nil, repeats: true)
}
#IBAction func greenButton() {
if stopFuncs { return }
compareColours(selectedColour: "GREEN")
changeColourLabel()
colourLabel.textColor = UIColor.green
}
#IBAction func blueButton() {
if stopFuncs { return }
compareColours(selectedColour: "BLUE")
changeColourLabel()
colourLabel.textColor = UIColor.blue
}
#IBAction func yellowButton() {
if stopFuncs { return }
compareColours(selectedColour: "YELLOW")
changeColourLabel()
colourLabel.textColor = UIColor.yellow
}
#IBAction func pinkButton() {
if stopFuncs { return }
compareColours(selectedColour: "GREEN")
changeColourLabel()
colourLabel.textColor = UIColor.red
}
func compareColours(selectedColour: String) {
if gameOver == false && counter >= 1 {
if selectedColour == colour {
score += 1
let scoreString = String(score)
scoreLabel.text = scoreString
}
else {
gameOver = true
}
}
else {
stopFuncs = true
}
}
func changeColourLabel() {
colour = colourProvider.randomColour()
colourLabel.text = colour
print(colour)
}
}
You already have IBOutlets to some of your labels.
For buttons you may need to set up both an IBaction and an IBOutlet.
An IBOutlet is a thing. A noun. It's a reference to a view object.
An IBAction is a verb. An action. It's code that gets called when something happens to a UIControl (tapping a button, moving a slider, changing the selected value on a segmented control, etc.)
The IBaction is a function that gets called when the user interacts with a control (For buttons you almost always link the action to the .touchUpInside control event.)
Naming your actions with names like pinkButton is a bad idea, and leads to confusion. I suggest renaming your actions to verbs, like handlePinkButton (or perhaps "pinkButtonTapped", but I prefer action names to be verbs.)
Note that when you rename an IBAction and/or IBOutlet you have to go into IB (IB = Interface Builder), select the connections inspector, remove the now-incorrect IBOutlet/IBAction and control-drag from the object to the updated outlet/action.
If you don't do that you'll get very confusing crashes at runtime:
Renaming IBOutlets in code without also changing them in IB gives the error "this class is not key value coding-compliant for the key old_outlet_name" (where "old_outlet_name" will be the old name of your outlet.) when you try to load the view controller from the storyboard. Renaming actions )
Renaming IBActions in code without also changing them in IB gives an error "unrecognized selector sent to instance big_hex_number" when you try to tap the button (or trigger the action for other controls.)
You should open your storyboard in the main editor and open the source in the assistant editor, then control-drag from your button in IB into your source code just below your other outlet at the top of your class.
Your button needs to be an IBOutlet rather than an IBAction. When you ctrl-drag from Interface Builder to your code, make sure you select the 'Outlet' option as shown below:
Then you can change the button title like this:
else {
startButton.setTitle("Button Title", for: .normal)
stopFuncs = true
}
I am a beginner of xcode programming. I am trying to do an action, when i click button from A view, the button of B view will be hidden. I already know i can use button.hidden = true; for self view controller but I don't know how to control button from other view.
Thanks
#IBAction func TestBut(sender: UIButton) {
setting.hidden = false
}
Before, you create a custom view with button, button action and a protocol as:
protocol CustomViewDelegate {
func buttonPressed (sender: AnyObject)
}
class CustomView: UIView {
#IBOutlet weak var button: UIButton!
var delegate: CustomViewDelegate!
override func awakeFromNib() {
super.awakeFromNib()
}
class func loadViewFromXib() -> CustomView {
return NSBundle.mainBundle().loadNibNamed("CustomView", owner: self, options: nil)[0] as! CustomView
}
#IBAction func buttonPressed(sender: AnyObject) {
self.delegate.buttonPressed(sender)
}
}
In your ViewController.
class ViewController: UIViewController, CustomViewDelegate {
var firstView: CustomView?
var secondView: CustomView?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.firstView = CustomView.loadViewFromXib()
self.secondView = CustomView.loadViewFromXib()
firstView!.frame = CGRectMake(0, 0, 100, 100)
secondView!.frame = CGRectMake(0, 200, 100, 100)
firstView!.delegate = self
secondView!.delegate = self
self.view.addSubview(firstView!)
self.view.addSubview(secondView!)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func buttonPressed(sender: AnyObject) {
if (sender as! UIButton) == self.firstView!.button {
self.secondView?.button.hidden = true
}else {
self.firstView?.button.hidden = true
}
}
}
the button has to be a property of the view( or view controller).
A call would look like this:
view.button.hidden = true