iOS Swift: Install and Unistall views programmatically - swift

How to install and uninstall views programmatically?
Example
Im my application I have a StackView containing three different Views: Character, Starship and Vehicle.
Now, I would like that at a certain condition, just one View will appear and the other two won't.
I'm not saying hiding and showing, but installing and uninstalling. Why? because if I keep my views installed my Xcode crash.
Any tips?

I believe the answer you're looking for is here - https://stackoverflow.com/a/36712325/2463875
If You install or uninstall view from storyboard, It is equivalent to add/remove view.

I am not sure if I understand it 100% correctly.. But if you want to add views to a stackview programatically, you can do it kind of the following way. You need to connect the UIStackView to your ViewController to have access to it. There you can add your UIViews depending on a condition.
I've created a playground example. So you can just copy it and run it in a playground and it will also show it visually to you. To make it easier, I've used UILabels. But it's pretty much the same with UIViews.
import UIKit
import PlaygroundSupport
class TestViewController : UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
title = "Test"
self.view.frame = CGRect(x: 0, y: 0, width: 320, height: 480)
self.view.backgroundColor = UIColor.brown
let stackview = UIStackView(frame: CGRect(x: 0, y: 0, width: 320, height: 480))
stackview.axis = .vertical
stackview.distribution = UIStackViewDistribution.fillEqually
let text = UILabel(frame: CGRect(x: 0, y: 0, width: 100, height: 20))
text.text = "Hello, i am a label"
let condition = true
if condition {
let text2 = UILabel(frame: CGRect(x: 0, y: 0, width: 100, height: 20))
text2.text = "Hello, i am another label"
stackview.addArrangedSubview(text2)
}
stackview.addArrangedSubview(text)
self.view.addSubview(stackview)
}
}
let testController = TestViewController()
PlaygroundPage.current.liveView = testController.view
testController

Related

Issue reusing the same UI Image code across multiple view controllers | Swift 5

I have the following function I use to customize the navigation bar across almost all the apps view controllers and table view controllers - instead of replicating the code numerous times I am looking for way to easily call the function on those view controllers needing it.
I have tried wrapping in extension UIViewController { } but run into a selector issue saying the following:
Argument of '#selector' cannot refer to local function
'Tapped(tapGestureRecognizer:)'
Code:
func navBar(){
// Profile Image
let containView = UIView(frame: CGRect(x: 0, y: 0, width: 40, height: 40))
let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 40, height: 40))
imageView.image = UIImage(url: URL(string: "test.com"))
imageView.contentMode = UIView.ContentMode.scaleAspectFit
imageView.layer.cornerRadius = 20
imageView.layer.masksToBounds = true
containView.addSubview(imageView)
let rightBarButton = UIBarButtonItem(customView: containView)
self.navigationItem.rightBarButtonItem = rightBarButton
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(imageTapped(tapGestureRecognizer:)))
imageView.isUserInteractionEnabled = true
imageView.addGestureRecognizer(tapGestureRecognizer)
}
#objc func imageTapped(tapGestureRecognizer: UITapGestureRecognizer) {
print("Profile Tapped")
}
How can this UIImage be seen in the navigation bar across various view controller without needing to rewrite the same code across all.
Lot a way to do it. I'll usually istance and personalize an UIViewController and use it around the whole app.
class baseController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
//call your navBar here
navbar()
}
func navBar(){
// Profile Image
let containView = UIView(frame: CGRect(x: 0, y: 0, width: 40, height: 40))
let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 40, height: 40))
imageView.image = UIImage(url: URL(string: "test.com"))
imageView.contentMode = UIView.ContentMode.scaleAspectFit
imageView.layer.cornerRadius = 20
imageView.layer.masksToBounds = true
containView.addSubview(imageView)
let rightBarButton = UIBarButtonItem(customView: containView)
self.navigationItem.rightBarButtonItem = rightBarButton
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(imageTapped(tapGestureRecognizer:)))
imageView.isUserInteractionEnabled = true
imageView.addGestureRecognizer(tapGestureRecognizer)
}
}
Now instance this class whenever you want and your controller will get your navBar() every time with this
class mineController:baseController {
//your code here...
}

Centering buttons programatically in Swift

I am trying to center Google Sign In and Sign Out buttons programmatically. In order to put both of them in the third quarter. I create 2 views that I wanted to put buttons to their center, in Storyboard.
GIDSignIn.sharedInstance()?.presentingViewController = self
GIDSignIn.sharedInstance().signIn()
let gSignIn = GIDSignInButton(frame: CGRect(x: 0, y: 0, width: loginView.frame.size.width, height: loginView.frame.size.height))
loginView.addSubview(gSignIn)
gSignIn.center = loginView.center
let gSignOut = UIButton(frame: CGRect(x: 0, y: 0, width: signOutView.frame.size.width, height: signOutView.frame.size.height))
gSignOut.backgroundColor = UIColor.white.withAlphaComponent(0)
gSignOut.setTitle("Sign Out", for: .normal)
gSignOut.setTitleColor(UIColor.red, for: .normal)
gSignOut.addTarget(self, action: #selector(self.signOut(_:)), for: .touchUpInside)
gSignOut.center = signOutView.center
self.signOutView.addSubview(gSignOut)
As you can see, also I am trying to resize buttons according to view size which helps me to resize buttons depend on device size.
Here is the second half of my storyboard.
Here is the simulator screen when I run the code.
Thanks.
Use auto-layout instead of setting frame.center, it will be more flexible.
Autolayout goes like below code.
GIDSignIn.sharedInstance()?.presentingViewController = self
GIDSignIn.sharedInstance().signIn()
let gSignIn = GIDSignInButton(frame: CGRect(x: 0, y: 0, width: loginView.frame.size.width, height: loginView.frame.size.height))
gSignIn.translatesautoresizingmaskintoconstraints = false
loginView.addSubview(gSignIn)
NSLayoutConstraint.activate([
gSignIn.leftAnchor.constraint(equalTo: loginView.leftAnchor),
gSignIn.rightAnchor.constraint(equalTo: loginView.rightAnchor),
gSignIn.topAnchor.constraint(equalTo: loginView.topAnchor),
gSignIn.bottomAnchor.constraint(equalTo: loginView.bottomAnchor)
])

Add drop shadow to xib view in Swift

I have added a card view from a Xib file which when tapped it slides from bottom-up to display the view.
I have am trying to customised the view and a drop shadow to the view which is not working.
The cornerRadius works fine otherwise.
func setupCard() {
menuCardVC = MenuCardVC(nibName:"MenuCardVC", bundle:nil)
menuCardVC.view.layer.shadowOpacity = 0.50
menuCardVC.view.layer.shadowRadius = 12
menuCardVC.view.layer.shadowColor = UIColor.black.cgColor
menuCardVC.view.layer.shadowOffset = CGSize.zero
menuCardVC.view.layer.cornerRadius = 25
self.addChild(menuCardVC)
self.view.addSubview(menuCardVC.view)
menuCardVC.view.frame = CGRect(x: 0, y: self.view.frame.height - cardHandleAreaHeight, width: self.view.bounds.width, height: cardHeight)
menuCardVC.view.clipsToBounds = true
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(VenueDetailsVC.handleCardTap(recognzier:)))
let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(VenueDetailsVC.handleCardPan(recognizer:)))
menuCardVC.handleArea.addGestureRecognizer(tapGestureRecognizer)
menuCardVC.handleArea.addGestureRecognizer(panGestureRecognizer)
}//end setupCard
Remove the below line of code,
menuCardVC.view.clipsToBounds = true
Since your view hierarchy is simple you could use the following technique. Also, you don't need to use clipsToBounds.
let superview = UIView(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
superview.backgroundColor = .white
let subview = UIView(frame: CGRect(x: 10, y: 10, width: 100, height: 100))
subview.backgroundColor = .red
// First provide the corner radius.
subview.layer.cornerRadius = 8
// Then provide the shadow parameters.
subview.layer.shadowOpacity = 1
// ...
// Do not clip to bounds since it will clip the shadow.
// ...
superview.addSubview(subview)

How to show all TextField in a view that stored in a array / Swift 5

I have a tapGesture, that every time when you click on it a new TextField displays and will saved in a array.
I can create multiple TextField and can store them in a Array, but I have no idea, how I can display them on a view.
At the Moment only the first one will be shown in the view.
I want to build something similar like snapchat with the text.
var myTextField: UITextField = UITextField(frame: CGRect(x: 0,y: 0, width: 300.0, height:30.0))
func addTapGestureToTextImageView() {
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTextImage))
textImageView.isUserInteractionEnabled = true
textImageView.addGestureRecognizer(tapGesture)
}
#objc func handleTextImage() {
textFields.append(myTextField)
let myTextField = UITextField(frame: CGRect(x: 0,y: 0, width: 300.0, height:30.0))
for i in 0..<textFields.count {
myTextField.tag = i
view.addSubview(textFields[i])
print(textFields[i])
}
print(textFields.count)
}
If you want to pile them vertically you have to increment the y with the height for each textField and hence change it's frame.
for i in 0..<textFields.count {
var field = textFields[i]
field.frame = CGRect(x: 0, y: 30*i, width: 300, height: 30)
view.addSubview(field)
print(textFields[i])
}
It will look something like this. But you will need to tweak it to properly align it.
My suggestion would be to put a UIStackView in the baseView and then use it's addArrangedSubview() and it will do the stacking for you.
Use stackView for this:
lazy var textFieldsView: UIStackView = {
let stackView = UIStackView()
stackView.spacing = 4
stackView.distribution = .fillEqually
stackView.alignment = .fill
stackView.frame.size.width = 300
view.addSubview(stackView)
return stackView
}()
#objc func handleTextImage() {
let myTextField = UITextField(frame: CGRect(x: 0, y: 0, width: 300.0, height: 30.0))
for pair in textFields.enumerated() {
pair.element.tag = pair.offset
textFieldsView.addArrangedSubview(pair.element)
}
// Update stackView frame if needed
}

UIButton Not Working After Updated to Xcode 8.3

After I updated to Xcode 8.3 the following code is not working in Xcode Playgrounds. Before on Xcode 8.2.1, I was able to click on the button and the action would follow, but now nothing is happening when I click the button.
import UIKit
import SpriteKit
import PlaygroundSupport
let view = SKView(frame: CGRect(x: 0, y: 0, width: 550, height: 575))
let scene = SKScene(size: CGSize(width: 550, height: 575))
scene.backgroundColor = UIColor.white
scene.scaleMode = SKSceneScaleMode.aspectFit
view.presentScene(scene)
class Responder: NSObject {
#objc func nextScene() {
print("Next button pressed!")
}
}
let responder = Responder()
let nextButton = UIButton(type: .system)
nextButton.frame = CGRect(x: 250, y: 557.5, width: 40, height: 15)
nextButton.setTitle("Next", for: .normal)
nextButton.titleLabel?.font = UIFont(name: "Marker Felt", size: 17)
nextButton.tintColor = .black
nextButton.addTarget(responder, action: #selector(Responder.nextScene), for: .touchUpInside)
view.addSubview(nextButton)
PlaygroundPage.current.liveView = view
EDIT: I updated my code so that it includes PlaygroundPage.current.liveView = view and the code for setting up the view and scene.
You do not set a live view for the playground in your code. Normally, a playground will execute the code and stop - it does not wait for input or events etc, unless you explicitly specify it should.
You should add the following line at the end of the playground to specify that canvas is a live view - provided it is, I can't know since you have not provided the implementation for canvas.
PlaygroundPage.current.liveView = canvas