How to prevent subview release self after call removeFromSuperview? - ios

I want to change with two subviews as button been clicked, the subview was created by StoryBoard, each subview has a button, click the button will bring another subview and hidden current one.
But I found when a subview call removeFromSuperview(), it will be release automatically, if I want to use this subview later, I need a var to point it.
Here is my code:
class ViewController: UIViewController {
#IBOutlet weak var secondView: UIView!
#IBOutlet weak var firstView: UIView!
var temp1: UIView?
var temp2: UIView?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
temp1 = firstView
temp2 = secondView
secondView.removeFromSuperview()
}
#IBAction func moveToSecond(_ sender: UIButton) {
firstView.removeFromSuperview()
view.insertSubview(secondView, at: 0)
secondView.isUserInteractionEnabled = true
}
#IBAction func moveToFirst(_ sender: UIButton) {
secondView.removeFromSuperview()
view.insertSubview(firstView, at: 0)
firstView.isUserInteractionEnabled = true
}
}
Without two temp var, subview will release after removeFromSuperview, and cause next insertSubview crash because it is nil.
So, How to prevent subview auto release?
Is there another way to change between two subviews created by StoryBoard graceful?
My mistake, I didn't notice that outlet is weak, everything is reasonable, when removeFromSuperview, no strong point is left to the sub view, than it release automatically.

You can use #IBOutlet var secondView: UIView! to create strong reference of the view.But as per your requirement I suggest not to remove it from the super view. Instead of that you should hide and show the views when needed as below.
class ViewController: UIViewController {
#IBOutlet var secondView: UIView!
#IBOutlet var firstView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
secondView.isHidden = true
}
#IBAction func moveToSecond(_ sender: UIButton) {
firstView.isHidden = true
secondView.isHidden = false
secondView.isUserInteractionEnabled = true
}
#IBAction func moveToFirst(_ sender: UIButton) {
secondView.isHidden = true
firstView.isHidden = false
firstView.isUserInteractionEnabled = true
}
}

Assuming your two views are one upon above , having same width and same height . In viewDidLoad . first set the firstView to front and secondView to back. Once button click i bring secondView to front and firstView to back , like so on.
import UIKit
class ViewController1: UIViewController {
#IBOutlet var secondView: UIView!
#IBOutlet var firstView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
self.view.bringSubview(toFront: firstView)
self.view.sendSubview(toBack: secondView)
}
#IBAction func moveToSecond(_ sender: UIButton) {
self.view.bringSubview(toFront: secondView)
self.view.sendSubview(toBack: firstView)
}
#IBAction func moveToFirst(_ sender: UIButton) {
self.view.bringSubview(toFront: firstView)
self.view.sendSubview(toBack: secondView)
}
}
If your two sub view's are in different position of the view. then you need to hide alternative View in respective button click
class ViewController: UIViewController {
#IBOutlet var secondView: UIView!
#IBOutlet var firstView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
secondView.isHidden = true
firstView.isUserInteractionEnabled = true
}
#IBAction func moveToSecond(_ sender: UIButton) {
firstView.isHidden = true
secondView.isHidden = false
secondView.isUserInteractionEnabled = true
}
#IBAction func moveToFirst(_ sender: UIButton) {
secondView.isHidden = true
firstView.isHidden = false
firstView.isUserInteractionEnabled = true
}
}

Related

How to remove popView if we tap anywhere in the background except tapping in popView in swift

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
}

How to update the layout (position) of a popover when its source view changes theirs in Swift?

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.

How to unhide the button after all switches in ON condition?

I have 2 UISwitch and a Button, in viewDidLoad, I set the button to be hidden and disabled, I want only my button to be not hidden if those 2 switch is in ON state, otherwise, I want my button to hide again. is there any method from UI Switch delegate that can be used ? how do I do that in Swift ?
here is the code I use
import UIKit
class AskingAuthorizationVC: UIViewController {
#IBOutlet weak var locationSwitch: DesignableSwitch!
#IBOutlet weak var notificationSwitch: DesignableSwitch!
#IBOutlet weak var nextButton: DesignableButton!
override func viewDidLoad() {
super.viewDidLoad()
// initial state
nextButton.isHidden = true
nextButton.isEnabled = false
notificationSwitch.isOn = false
locationSwitch.isOn = false
}
#IBAction func signUpButtonDidPressed(_ sender: Any) {
performSegue(withIdentifier: "toAuthenticationVC", sender: nil)
}
}
Hook both of the UISwitch-s as IBActions & IBOutlets
#IBAction func oweSwitch(_ sender: UISwitch) {
self.mybutton.isHidden = !(switch1.isOn && switch2.isOn)
}

Passing an object with data from one view to another view in Swift using Segue

I am trying to pass an object with data from one view to another view using Swift4. However, I get this problem when trying to print.
Here is the message that I get:
2018-03-21 21:47:03.542578-0400 Sudoku[64427:1070575] <UIView:
0x7fd28bc0b4c0; frame = (0 0; 414 736); autoresize = W+H; layer =
<CALayer: 0x604000038500>>'s window is not equal to
<Sudoku.setupSudoku: 0x7fd28bf256c0>'s view's window!
Here is the picture of my View. Identifier for segue: setupScreen
Here is the code in setPlayer:
class setPlayer: UIViewController {
var singleUser:playerInfor=playerInfor()
var randomGame:Int = -1
var playerName:String=""
#IBOutlet weak var txtPlayerName: UITextField!
var sizeOfSudoku:Int = 9 //for project 1, let take one size of sudoku
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor(patternImage: UIImage(named: "Communication-Network-Vector-Illustration.jpg")!)
self.txtPlayerName.layer.borderWidth=2.0
self.txtPlayerName.layer.borderColor=UIColor.blue.cgColor
// Do any additional setup after loading the view.
}
func randomNumber(){
self.randomGame=Int(arc4random_uniform(3)+1)
}
func initialize(){
//if(size)
if txtPlayerName.text?.isEmpty ?? true {
self.playerName="Player1"
} else {
self.playerName=self.txtPlayerName.text!
}
self.singleUser=playerInfor(playerName:self.playerName,size:self.sizeOfSudoku,time:0, randomSudoku:self.randomGame)
//print (self.singleUser.size)
}
#IBAction func startButton(_ sender: Any) {
initialize()
performSegue(withIdentifier: "setupScreen", sender: self.singleUser)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "setupScreen"{
if let destination=segue.destination as?setupSudoku{
destination.objectPlayer=sender as?playerInfor
print(singleUser.playerName)
}
}
}
#IBAction func size9(_ sender: Any) {
self.sizeOfSudoku=9
}
#IBAction func Size6(_ sender: Any) {
self.sizeOfSudoku=9
}
Here is playerInfor
import Foundation
import UIKit
class playerInfor{
var playerName:String=""
var size:Int=0
var time:Int=var randomSudoku:Int=0
init(playerName:String,size:Int,time:Int,randomSudoku:Int) {
self.playerName=playerName
self.size=size
self.time=time
self.randomSudoku=randomSudoku
}
}
Here is setupSudoku
class setupSudoku: UIViewController , UICollectionViewDelegate, UICollectionViewDataSource{
#IBOutlet weak var stackViewButtons: UIStackView!
#IBOutlet weak var myCollectionView: UICollectionView!
var newNumber=0
var objectPlayer:playerInfor!
override func viewDidLoad() {
super.viewDidLoad()
print(self.objectPlayer?.time)
}
}
You have setup your segue incorrectly in your storyboard, and it is attempting to segue twice. To confirm this is the case, select your segue in your storyboard and check if your button highlights, like this:
To resolve, delete this segue and create a new one, by control-dragging from the view controller (not the button) to the destination, like so:
Make sure to reset the new segues identifier back to setupScreen. Then you should be good to go!

How to get reference to UIView in scene dock?

I have UIViewController with 2 UIViews (scenes) in dock:
How can i display it in whatever place I need in my UIViewController? In other words, how can I get reference to this views from within my controller?
Swift 3, Xcode 8
1. Create a Reference
You can drag the UIViews to your ViewController and create outlets for them.
2. To Show UIView
You want to add it to the view as a subview. I hooked up those two buttons you see in my scene:
class ViewController: UIViewController {
#IBOutlet var view1: UIView!
#IBOutlet var view2: UIView!
// SHOW THE VIEWS
#IBAction func showView1(_ sender: UIButton) {
view.addSubview(view1)
view1.center = view.center
}
#IBAction func showView2(_ sender: UIButton) {
view.addSubview(view2)
view2.center = CGPoint(x: view.center.x, y: 100)
}
}
3. To Hide the UIViews
You will have to remove them from your view. I created actions for the Close buttons on the UIViews:
class ViewController: UIViewController {
#IBOutlet var view1: UIView!
#IBOutlet var view2: UIView!
// SHOW THE VIEWS
#IBAction func showView1(_ sender: UIButton) {
view.addSubview(view1)
view1.center = view.center
}
#IBAction func showView2(_ sender: UIButton) {
view.addSubview(view2)
view2.center = CGPoint(x: view.center.x, y: 100)
}
// HIDE THE VIEWS
#IBAction func hideView1(_ sender: UIButton) {
view1.removeFromSuperview()
}
#IBAction func hideView2(_ sender: UIButton) {
view2.removeFromSuperview()
}
}
Hope this helps!
Within my UIViewController's code keep references using #IBOutlet:
#IBOutlet weak var dockView: UIView!

Resources