add action to programmatically created uibuttons - swift3

I have written a class file to create a topBar for my app, I also add in buttons to the top bar after adding target to the buttons added, the same is not triggered
the line menuButton.addTarget(self, action: #selector(showMenu), for: .touchUpInside) does not trigger the function showMenu
I create a TN object(from class TopNav) in the main view file and add it to the view, but the menu button does not trigger the tap
import Foundation
import UIKit
class TopNav{
var topView: UIView = UIView()
var menuButton: UIButton = UIButton()
#objc func showMenu(sender: UIButton!) {
print("show Menu")
}
func position(){
let bounds = UIScreen.main.bounds
let width = bounds.size.width
topView = UIView(frame: CGRect(x: 0.0, y: 0.0, width: width, height: 60.0))
topView.backgroundColor = UIColor.init(colorLiteralRed: 66/255, green: 74/255, blue: 87/255, alpha: 1.0)
menuButton = UIButton(frame: CGRect(x: (width-40), y: 20.0, width: 30.0, height: 30.0))
menuButton.setBackgroundImage( UIImage(named:"menu"), for: .normal)
menuButton.setTitle("", for: UIControlState.normal)
menuButton.addTarget(self, action: #selector(showMenu), for: .touchUpInside)
topView.addSubview(menuButton)
}
}

You may try this.
class TopNav {
func position() {
...
menuButton.addTarget(self, action: #selector(TopNav.showMenu), for: .touchUpInside)
...
}
func showMenu() {
//your code
}
}

Related

How do you handle onChange() of #EnvironmentObject in an SKScene, and not break the scene rendering?

In the test code below the blue ellipse is drawn, but the red ellipse is not drawn. Is there a way to handle the onChange and call a drawing function in a SpriteKit scene? (The print statement inside handleDataChange() does print to the console.)
The gameEnvData is being changed in a separate scene.
import SwiftUI
import SpriteKit
struct NextPieceView: View {
#EnvironmentObject var gameEnvData: GameData
var scene: NextPieceScene {
let scene = NextPieceScene()
scene.size = CGSize(width: 200, height: 100)
scene.gameData = gameEnvData
return scene
}
var body: some View {
SpriteView(scene: scene)
.onChange(of: gameEnvData.pieceCount, perform: { _ in
scene.handleDataChange()
})
}
}
import SpriteKit
class NextPieceScene: SKScene {
var gameData: GameData = GameData()
override func didMove(to view: SKView) {
drawTestShape(position: CGPoint(x: 25, y: 50), color: .blue) // this works
}
func handleDataChange() {
print("handleDataChange called")
drawTestShape(position: CGPoint(x: 75, y: 50), color: .red) // does not draw
}
func drawTestShape(position: CGPoint, color: UIColor) {
let shapeNode = SKShapeNode(ellipseOf: CGSize(width: 40, height: 40))
shapeNode.fillColor = color
shapeNode.position = position
addChild(shapeNode)
}
}
Fixes:
Used the #State wrapper when declaring the scene
The scene could no longer be a computed property, due to wrapper
Now passing the #EO gameEnvData in via the onChange handler
struct NextPieceView: View {
#EnvironmentObject var gameEnvData: GameData
#State var scene: NextPieceScene = NextPieceScene(
size: CGSize(width: 200, height: 100))
var body: some View {
SpriteView(scene: scene)
.onChange(of: gameEnvData.pieceCount, perform: { _ in
scene.handleDataChange(gameEnvData: gameEnvData) })
}
}

SwiftUI - How to get notified when dragging and releasing on an view

I have two circles on my screen (top and bottom). I want the user to press down on the top circle, and drag to the bottom one.
I'm not dragging and dropping (I don't want the UI to change).
I just want to know that the user started on the top one, and released their finger on the bottom circle. When the user releases their finger on the bottom one.
I haven't been able to find my answer from other questions.
Current code
import SwiftUI
struct ContentView: View {
var body: some View {
VStack {
Circle()
.fill()
.foregroundColor(.blue)
.frame(width: 100, height: 100)
.gesture(
DragGesture(minimumDistance: 0, coordinateSpace: .named("mySpace"))
.onChanged { value in
}
.onEnded { value in
// if value.location == endPoint {
// print("user started press on blue and ended on green")
// }
}
)
Spacer()
Circle()
.fill()
.foregroundColor(.green)
.frame(width: 100, height: 100)
}.coordinateSpace(name: "mySpace")
}
}
Screenshot
Would appreciate any help!
Here I could find a way for get notified wether this 2 Circles are in some part inside each other:
import SwiftUI
struct ContentView: View {
typealias OffsetType = (offset: CGSize, lastOffset: CGSize)
#State private var objects: [OffsetType] = [(offset: CGSize(width: 0.0, height: -200.0), lastOffset: CGSize(width: 0.0, height: -200.0)),
(offset: CGSize(width: 0.0, height: 200.0), lastOffset: CGSize(width: 0.0, height: 200.0))]
var body: some View {
ZStack {
CircleView(color: Color.blue)
.offset(objects[0].offset)
.gesture(dragGesture(indexOfObject: 0))
CircleView(color: Color.green)
.offset(objects[1].offset)
.gesture(dragGesture(indexOfObject: 1))
}
.animation(Animation.easeInOut(duration: 0.1))
}
func dragGesture(indexOfObject: Int) -> some Gesture {
DragGesture(minimumDistance: 0.0, coordinateSpace: .global)
.onChanged() { value in
objects[indexOfObject].offset = CGSize(width: objects[indexOfObject].lastOffset.width + value.translation.width,
height: objects[indexOfObject].lastOffset.height + value.translation.height)
}
.onEnded() { value in
objects[indexOfObject].lastOffset = CGSize(width: objects[indexOfObject].lastOffset.width + value.translation.width,
height: objects[indexOfObject].lastOffset.height + value.translation.height)
objects[indexOfObject].offset = objects[indexOfObject].lastOffset
distance()
}
}
func distance() {
if pow(pow((objects[1].offset.width - objects[0].offset.width), 2.0) + pow((objects[1].offset.height - objects[0].offset.height), 2.0), 0.5) <= 100 { print("same place!") }
}
}
struct CircleView: View {
let color: Color
var body: some View {
Circle()
.fill(color)
.frame(width: 100, height: 100)
}
}

How to move scrollview with buttons only in SwiftUI

Previously I did with Swift4 UIScrollView which scrolled with buttons and x offset.
In Swift4 I have:
Set Scrolling Enabled and Paging Enabled to false.
Created the margins, offsets for each frame in UIScrollView and changed the position with buttons Back and Next.
Here is the code:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var buttonSound: UIButton!
#IBOutlet weak var buttonPrev: UIButton!
#IBOutlet weak var buttonNext: UIButton!
#IBOutlet weak var scrollView: UIScrollView!
var levels = ["level1", "level2", "level3", "level4"]
let screenWidth = UIScreen.main.bounds.width
let screenHeight = UIScreen.main.bounds.height
var currentLevel = 1
var previousLevel: Int? = nil
override func viewDidLoad() {
super.viewDidLoad()
//Defining the Various Swipe directions (left, right, up, down)
let swipeLeft = UISwipeGestureRecognizer(target: self, action: #selector(self.handleGesture(gesture:)))
swipeLeft.direction = .left
self.view.addGestureRecognizer(swipeLeft)
let swipeRight = UISwipeGestureRecognizer(target: self, action: #selector(self.handleGesture(gesture:)))
swipeRight.direction = .right
self.view.addGestureRecognizer(swipeRight)
addHorizontalLevelsList()
customizeButtons()
resizeSelected()
}
func addHorizontalLevelsList() {
var frame : CGRect?
for i in 0..<levels.count {
let button = UIButton(type: .custom)
let buttonW = screenWidth/3
let buttonH = screenHeight/2
frame = CGRect(x: CGFloat(i+1) * (screenWidth/2) - (buttonW/2),
y: buttonH - 100,
width: buttonW,
height: buttonH)
button.frame = frame!
button.tag = i+1
button.backgroundColor = .lightGray
button.addTarget(self, action: #selector(selectTeam), for: .touchUpInside)
button.setTitle(levels[i], for: .normal)
scrollView.addSubview(button)
}
scrollView.contentSize = CGSize(width: (screenWidth/2 * CGFloat(levels.count)),
height: screenHeight)
scrollView.backgroundColor = .clear
self.view.addSubview(scrollView)
}
func customizeButtons(){
buttonPrev.frame = CGRect(x: 0,
y: (screenHeight/2) - 40,
width: 80, height: 80)
buttonNext.frame = CGRect(x: screenWidth - 80,
y: (screenHeight/2) - 40,
width: 80, height: 80)
buttonPrev.superview?.bringSubviewToFront(buttonPrev)
buttonNext.superview?.bringSubviewToFront(buttonNext)
}
#objc func selectTeam(button: UIButton) {
button.transform = CGAffineTransform(scaleX: 0.6, y: 0.6)
UIView.animate(withDuration: 1.0,
delay: 0,
usingSpringWithDamping: CGFloat(0.20),
initialSpringVelocity: CGFloat(6.0),
options: UIView.AnimationOptions.allowUserInteraction,
animations: {
button.transform = CGAffineTransform.identity
},
completion: { Void in() }
)
print(levels[button.tag])
let vc = PopTypeVC(nibName: "PopTypeVC", bundle: nil)
vc.modalPresentationStyle = UIModalPresentationStyle.overCurrentContext
self.present(vc, animated: true)
}
#IBAction func prevLevel(_ sender: Any) {
if currentLevel > 0 {
currentLevel -= 1
scroll()
}
}
#IBAction func nextLevel(_ sender: Any) {
if currentLevel < levels.count {
currentLevel += 1
scroll()
}
}
func scroll(){
print(currentLevel)
print(previousLevel as Any)
scrollView.setContentOffset(CGPoint(x: currentLevel * Int(screenWidth/2), y: 0), animated: true)
resizeSelected()
}
// The #objc before func is a must, since we are using #selector (above)
#objc func handleGesture(gesture: UISwipeGestureRecognizer) -> Void {
if gesture.direction == UISwipeGestureRecognizer.Direction.right {
prevLevel(self)
}
else if gesture.direction == UISwipeGestureRecognizer.Direction.left {
nextLevel(self)
}
}
func resizeSelected(){
if previousLevel != nil {
let previousFrame = CGRect(x:CGFloat(previousLevel!) * (screenWidth/2) - (screenWidth/3)/2,
y: (screenHeight/2) - 100,
width: screenWidth/3,
height: screenHeight/2)
scrollView.viewWithTag(previousLevel!)?.frame = previousFrame
}
let currentFrame = CGRect(x: CGFloat(currentLevel) * (screenWidth/2) - (screenWidth/3)/2 - 10,
y: (screenHeight/2) - 110,
width: screenWidth/3 + 20,
height: screenHeight/2 + 20)
scrollView.viewWithTag(currentLevel)?.frame = currentFrame
previousLevel = currentLevel
}
}
The problem is I can't do this with SwiftUI:
struct ContentView: View {
static var levels = ["level1",
"level2",
"level3",
"level4"]
var currentLevel = 1
var previousLevel: Int? = nil
let screenW = UIScreen.main.bounds.width
let screenH = UIScreen.main.bounds.height
let margin1 = 50
let margin2 = 100
let margin3 = 20
let sceneButtonW = 100
let buttonPadding = 40
var body: some View {
ZStack {
// Horizontal list
VStack {
Spacer()
.frame(height: margin2)
ScrollView(.horizontal, showsIndicators: false) {
HStack{
Spacer()
.frame(width: buttonPadding + sceneButtonW/2)
ForEach(0..<ContentView.levels.count) { i in
cardView(i: i).tag(i+1)
}
Spacer()
.frame(width: buttonPadding + sceneButtonW/2)
}
}
Spacer()
.frame(height: margin3)
}
}
.background(Image("bg")
.resizable()
.edgesIgnoringSafeArea(.all)
.aspectRatio(contentMode: .fill))
}
}
Question: Are there any methods to disable automatic scrolling at all and use offsets at ScrollView with SwiftUI?
This already built solution for SwiftUI
https://github.com/fermoya/SwiftUIPager
However, there is no real example.

How to add spacing to Left BarButtonItem Image with Swift 3

I would like to add space to the left of the bar button.
So, I add this code.
navbar.autoresizingMask = [.flexibleWidth, .flexibleBottomMargin, .flexibleRightMargin]
navbar.delegate = self
UINavigationBar.appearance().barTintColor = UIColor(red: 0.0/255.0, green:49.0/255.0, blue:79.0/255.0, alpha:0.1)
UINavigationBar.appearance().tintColor = UIColor.white
UINavigationBar.appearance().isTranslucent = true
UINavigationBar.appearance().titleTextAttributes = [NSForegroundColorAttributeName : UIColor.white]
navItem.title = prefs.value(forKey: "PROVIDER_NAME") as! String?
let image = UIImage(named: "back_image")
navItem.leftBarButtonItem = UIBarButtonItem(image: image, style: .plain, target: self, action: #selector(addTapped))
After adding this code, the button image does not show well and looks no good.
Could anyone help me to solve this problem?
Hi Code given below might be useful for you.
extension UIBarButtonItem {
class func itemWith(colorfulImage: UIImage?, target: AnyObject, action: Selector) -> UIBarButtonItem {
let button = UIButton(type: .custom)
button.setImage(colorfulImage, for: .normal)
button.frame = CGRect(x: 0.0, y: 0.0, width: 44.0, height: 44.0)
button.imageEdgeInsets = UIEdgeInsetsMake(0, -50, 0, 0)
button.addTarget(target, action: action, for: .touchUpInside)
let barButtonItem = UIBarButtonItem(customView: button)
return barButtonItem
}
}
You can increase or decrease Left Padding for UIEdgeInsetsMake(0, -50, 0, 0) as per your requirement for left padding.
You can use above extension in your code as describe below.
self.navigationItem.leftBarButtonItem = UIBarButtonItem.itemWith(colorfulImage: UIImage(named: "ic_back")?.withColor(UIColor.white), target: self, action: #selector(btnBackClicked))
func btnBackClicked() {
print("your code here on back button tapped.")
}

Hiding of dynamic button with Swift3

I want to hide a dynamic button with Swift 3 but I can't get it done. I just started with Swift but have some time pressure considering development of an app.
After pressing ButtonGen1 I want to hide this button and view 2 other buttons namely ButtonGen2_1 and ButtonGen2_2.
Using Google I tried several things. For example the following:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
createButtonGen1()
buttonGen1?.removeFromSuperview
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func createButtonGen1 ()
{
let buttonGen1 = UIButton(frame: CGRect(x:100, y: 100, width: 100, height: 50))
buttonGen1.setTitle("Is the original visit to this outlet to Eat or Drink?", for: .normal)
buttonGen1.setTitleColor(UIColor.orange, for: .normal)
buttonGen1.addTarget(self, action: #selector(buttonActionGen1), for: .touchUpInside)
self.view.addSubview(buttonGen1)
}
func createButtonGen2_1 ()
{
let buttonGen2_1 = UIButton(frame: CGRect(x:50, y: 100, width: 100, height: 150))
buttonGen2_1.setTitle("Is the name Restaurant?", for: .normal)
buttonGen2_1.setTitleColor(UIColor.blue, for: .normal)
buttonGen2_1.addTarget(self, action: #selector(buttonAction2_1), for: .touchUpInside)
self.view.addSubview(buttonGen2_1)
}
func createButtonGen2_2 ()
{
let buttonGen2_2 = UIButton(frame: CGRect(x:150, y: 100, width: 100, height: 150))
buttonGen2_2.setTitle("Is the name Restaurant?", for: .normal)
buttonGen2_2.setTitleColor(UIColor.red, for: .normal)
buttonGen2_2.addTarget(self, action: #selector(buttonAction2_2), for: .touchUpInside)
self.view.addSubview(buttonGen2_2)
}
func buttonActionGen1(sender: UIButton!) {
// ----> HOW TO HIDE BUTTON buttonGen1
buttonGen1.hidden = true
createButtonGen2_1 ()
createButtonGen2_2 ()
}
func buttonAction2_1(sender: UIButton!)
{
print("Button 2_1 tapped")
}
func buttonAction2_2(sender: UIButton!)
{
print("Button 2_2 tapped")
}
HOW TO HIDE BUTTON buttonGen1 ?!?!?!?
Your help is much appreciated!!!