I am trying to make a reusable PopUp view controller composed of the following:
a static part (the top one) with an image and a label
a dynamic part (lower one) that can contain either 1 large button, or 2 smaller one side by side or 2 smaller buttons on top of one another.
It should look like this:
For now here is what I did:
Created a PopUpViewController class
In the storyboard, added a VC with the PopUpViewController class
Added the outlets for the static part
And now, I don't know how to proceed.
The idea of showing/hiding objects depending on the style of the popup (1 or 2 buttons etc.) seems horrible to me.
I don't think I can use xibs ?
I wish I could use storyboards because I am unable to do the auto layout via code.
Is code still the way to go ?
You can create 3 buttons inside your code. And using an if statement either show the 2 small buttons or the big one. Otherwise i think u can add 2 "View" objects in your storyboard and add 2 buttons in one and 1 button on the other and show and hide the "View" you want again with an if statement.
Here is an example :
import UIKit
class ViewController: UIViewController {
var flag = false
var buttonSmallOne: UIButton!
var buttonSmallTwo: UIButton!
var buttonBigOne: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// Create the 3 buttons
buttonSmallOne = UIButton(frame: CGRect(x: self.view.bounds.width/2 - 50, y: self.view.bounds.height/2, width: 50, height: 50))
buttonSmallOne.backgroundColor = .green()
buttonSmallOne.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
buttonSmallTwo = UIButton(frame: CGRect(x: self.view.bounds.width/2 + 50, y: self.view.bounds.height/2, width: 50, height: 50))
buttonSmallTwo.backgroundColor = .green()
buttonSmallTwo.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
self.view.addSubview(buttonSmallOne)
self.view.addSubview(buttonSmallTwo)
buttonBigOne = UIButton(frame: CGRect(x: self.view.bounds.width/2-100, y: self.view.bounds.height/2, width: 100, height: 50))
buttonBigOne.backgroundColor = .red()
buttonBigOne.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
self.view.addSubview(buttonBigOne)
buttonBigOne.isHidden = true
buttonBigOne.isEnabled = false
}
func buttonAction(sender: UIButton!) {
if flag == false {
// Hide
buttonSmallOne.isHidden = true
buttonSmallOne.isEnabled = false
buttonSmallTwo.isHidden = true
buttonSmallTwo.isEnabled = false
// Show
buttonBigOne.isHidden = false
buttonBigOne.isEnabled = true
flag = true
}else{
// Show
buttonSmallOne.isHidden = false
buttonSmallOne.isEnabled = true
buttonSmallTwo.isHidden = false
buttonSmallTwo.isEnabled = true
// Hide
buttonBigOne.isHidden = true
buttonBigOne.isEnabled = false
flag = false
}
}
}
I would advise you to create a class for the container (for the dynamic content).
Create one class with a stack view in it(you can change from horizontal to vertical, based on button logic ).
Create third class with button and label in it.
Construct the container with the right views based on Enum configuration.
Create everything from code and added this container subview to your popOver on creation.
If you have to support iOS 8 you can use this with reachability(check for ios version). It works great for this kind of problems. And later on you can just rename it and delete iOS 8 code from the project. It has the same API as a stackView.
https://github.com/tomvanzummeren/TZStackView
Related
I have read the manual (https://material.io/develop/ios/components/buttons/), but still have not understood how to do it.
class FloatingButtonController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let floatingButton = MDCFloatingButton()
floatingButton.setImage( UIImage(named: "plus"), for: .normal)
floatingButton.backgroundColor = .white
floatingButton.setElevation(ShadowElevation(rawValue: 6), for: .normal)
floatingButton.addTarget(self, action: #selector(btnFloatingButtonTapped(floatingButton:)), for: .touchUpInside)
self.view.addSubview(floatingButton)
}
#objc func btnFloatingButtonTapped(floatingButton: MDCFloatingButton){
floatingButton.collapse(true) {
floatingButton.expand(true, completion: nil)
}
}
}
My project on the screen. As you can see - the button is missing on the screen. When you click on a blank screen errors appear.
Button touch target does not meet minimum size guidlines of (48, 48). Button: >, Touch Target: {0, 0}
Screenshot of my project
Tell me, please, what am I doing wrong? And how do I get the job right?
You didn't set the frame to button. You need to specify (x, y) position to place button in view.
floatingButton.frame = CGRect(x: 300, y: 300, width: 48, height: 48)
To add button target the button should have size atlease 48x48.
You could constraint it manually or set frame.
OR you could add button via Storyboard too.
My view (in the Main.storyboard) contains another UIView which takes only about 80% of the screen's height (set with constraints).
In this second view (I call it the viewContainer) I want to display different views, which works with
viewContainer.bringSubview(toFront: someView)
I created a new group in my project which contains 3 UIViewController classes and 3 xib files which I want to display in my viewContainer.
For each of those xib files I changed the background color to something unique so I can tell if it's working. And it does so far.
Now I tried adding a UIButton to the first UIViewController class and added an #IBAction for it. That just prints text to the console.
When I run the app I can switch between my 3 classes (3 different background colors) and I can also see and click the button I added to the first class when I select it.
But the code is never executed and my console is empty. Is that because my 3 other Views are not shown in my Main.storyboard?
SimpleVC1.swift
import UIKit
class SimpleVC1: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func onButtonPressed(_ sender: Any) {
print("test")
}
}
Try using a target instead (and programmatically create the button), it might be easier:
import UIKit
class SimpleVC1: UIViewController {
var button = UIButton()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(button)
button.frame.size = CGSize(width: 100, height: 100)
button.center = CGPoint(x: 100, y: 100)
button.setTitle("Control 1", for: .normal)
button.addTarget(self, action: #selector(control1), for: .touchUpInside)
button.backgroundColor = UIColor.blue
button.setTitleColor(UIColor.white, for: .normal)
}
#objc func control1() {
//Add your code for when the button is pressed here
button.backgroundColor = UIColor.red
}
}
I have created a button programatically and want to add a touch event handler. I have tried all the tricks I have found online but the desired function is just never triggered.
upgradeNowView = UIButton()
upgradeNowView.setTitle("Upgrade", forState: .Normal)
upgradeNowView.contentMode = UIViewContentMode.ScaleToFill
upgradeNowView.frame = CGRect(x: upgradeNowView.frame.minX, y: -50 , width: 320, height: 50)
upgradeNowView.userInteractionEnabled = true
upgradeNowView.sendActionsForControlEvents(.TouchUpInside)
upgradeNowView.addTarget(self, action: #selector(MainViewContainer.upgradeTapped(_:)), forControlEvents: .TouchUpInside)
side_menu_controller?.view.addSubview(upgradeNowView)
and defined this
func upgradeTapped(sender: UIButton)
{
UpgradeViewController.openUpgrade()
}
But this just never gets called. Any help would be appreciated.
You need to define upgradeTapped function in class of side_menu_controller instance. I guess, it's SideMenuController class.
I found out this was caused by the fact that I was placing the button next to the view but outside its bounds (just on top):
upgradeNowView.frame = CGRect(x: upgradeNowView.frame.minX, y: -50 , width: 320, height: 50)
There are two ways around this:
Move the button inside the bounds of the parent view, or
Handle touches outside the view by overriding "hitTest" as discussed here
EXPLANATION:
1) I initiate the button above the class :
var myButton = UIButton()
2) I then create x amount of buttons depending on how many items are in an array
for letter in arrayOfLetters {
myButton = UIButton(frame: CGRect(x: buttonX, y: 500, width: someFloat, height: someFloat))
buttonX = buttonX + thirdFloat //spacing
myButton.layer.cornerRadius = 5
myButton.backgroundColor = UIColor.darkGrayColor()
myButton.setTitle("\(letter)", forState: UIControlState.Normal)
myButton.titleLabel?.text = "\(letter)"
myButton.addTarget(self, action: "myButtonPressed:", forControlEvents: UIControlEvents.TouchUpInside)
self.view.addSubview(myButton)
}
3) Later on once the user has pressed the correct buttons, I call a function to refresh and I want to remove all buttons. This is so I can generate new buttons based on new array
myButton.removeFromSuperview()
ISSUE:
However this does nothing, I tried giving the buttons a tag and deleting buttons by tag == tagId but nothing happened.
I can delete all views but it deletes everything else,
I tried this but again nothing happends
var buttons = [myButton]
for button in buttons as! [UIButton] {
button.removeFromSuperview()
}
I want to delete all buttons I added and i'm either not deleting them or deleting everything in the view
EASY FIX:
1) Inside Class, out of any func:
var buttonsArray = [UIButton]()
2) Declare myButton here and add the append line:
for letter in arrayOfLetters {
var myButton = UIButton(frame: CGRect(x: buttonX, y: 500, width: someFloat, height: someFloat))
...
self.buttonsArray.append(myButton)
}
3) To remove buttons:
for btn in buttonsArray {
btn.removeFromSuperview()
}
EXPLANATION:
Always store in an array the elements you create with a loop if you want to easily remove all of them at once.
i hope it will help for you give tag for your button while you are adding delete that button
for button in self.view.subviews {
if button.tag == 100 {
button.removeFromSuperview()
}
}
you are re-assigning the myButton variable as you loop.
Remove this line:
var myButton = UIButton()
add a property at the top to hold the buttons:
var buttons:[UIButton]()
then later in your code:
for letter in arrayOfLetters {
var myButton = UIButton(frame: CGRect(x: buttonX, y: 500, width: someFloat, height: someFloat))
buttonX = buttonX + thirdFloat //spacing
myButton.layer.cornerRadius = 5
myButton.backgroundColor = UIColor.darkGrayColor()
myButton.setTitle("\(letter)", forState: UIControlState.Normal)
myButton.titleLabel?.text = "\(letter)"
myButton.addTarget(self, action: "myButtonPressed:", forControlEvents: UIControlEvents.TouchUpInside)
self.view.addSubview(myButton)
self.buttons.append(myButton)
}
Then you should be able to remove like so:
for button in self.buttons {
button.removeFromSuperview()
}
This should resolve your issue, But it's important to understand what the problem was, so you define a new button as a property with this line: var myButton = UIButton(). This holds ONE button. as you are looping through creating buttons you are overriding the button with the next one.
So if anything, Calling myButton.removeFromSuperView() will remove the last one you created. Doing it the way I suggested means you create a button and add it to an array of buttons, so later you can iterate over the array referencing each button in turn and remove it.
I've just started to code my app in Swift 2 and avoiding the use of XIBs and storyboards.
However, I am unable to replicate the following feature. It's exactly what I wanted.
I've tried creating a UIView to perform the following using .backgroundColor and it works, however, I am unable to link it to my UIViewControllers. Just wondering how is it done? How do I link my UIView to my UIViewController?
Codes:
let subFrame : CGRect = CGRectMake(0,screenHeight*1/2.75,screenWidth,screenHeight)
var loginView = SignUpViewController()
let signUpView: UIView = UIView(frame: subFrame)
signUpView.backgroundColor = UIColor.redColor()
//Controls what each segment does
switch segmentView.indexOfSelectedSegment {
case 0:
self.view.addSubview(signUpView)
case 1:
self.view.addSubview(loginView)
default:
break;
}
I'm not even sure if .view.addSubview(xxx) overwrites/replaces the original subview if it is not this way. Is this the right way to do it?
Do not just start coding an app if you are not familiar with simple things of the OOP (Object-Oriented-Programming) language like Swift. This is not the way how to learn a programming language. Sure you could learn while experimenting but it is better to understand the book first before starting with more complex stuff. Read a few more pages of the Swift book from Apple. Most classes for iOS development are still Objective-C wrapped classes (reference type because the top superClass is probably NSObject; keep this in mind).
Here is the code example you wanted:
class ViewController: UIViewController {
let firstView = UIView()
let secondView = UIView()
let segmentedControlView = UISegmentedControl(items: ["firstView", "secondView"])
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.whiteColor() // we need this for the playground
/* setup your view here */
/* add your sigment logic somewhere */
self.view.addSubview(self.segmentedControlView)
self.view.addSubview(self.firstView)
self.view.addSubview(self.secondView)
self.segmentedControlView.frame = CGRect(x: 0, y: 20, width: self.view.frame.width, height: 44)
self.segmentedControlView.selectedSegmentIndex = 0 // enable the first segment
self.segmentedControlView.addTarget(self, action: "segmentIndexChanged:", forControlEvents: UIControlEvents.ValueChanged)
/* add your own frame calculation here */
/* I prefer AutoLayout, but for the example static frames will be fine */
self.firstView.frame.origin = CGPoint(x: 0, y: self.segmentedControlView.frame.origin.y + self.segmentedControlView.frame.height)
self.firstView.frame.size = CGSize(width: self.view.frame.width, height: self.view.frame.height - self.segmentedControlView.frame.origin.y)
// to prevent same code, we just copy the same frame from the firstView
// both will sit in the same place
self.secondView.frame = self.firstView.frame
/* lets add some colors so we'll see our views */
self.firstView.backgroundColor = UIColor.blueColor()
self.secondView.backgroundColor = UIColor.redColor()
self.secondView.hidden = true // when intializer the secondView is not visible
}
func segmentIndexChanged(sender: UISegmentedControl) {
switch sender.selectedSegmentIndex {
case 0:
self.firstView.hidden = false
self.secondView.hidden = true
case 1:
self.firstView.hidden = true
self.secondView.hidden = false
default:
break;
}
}
}
If you do not understand a function, should should look up its definition in the developer docs. (Like: addSubview)