There are 2 functions:
func configure() {
let scrollView = UIScrollView(frame: CGRect(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.height))
scrollView.contentSize = CGSize(width: view.frame.size.width, height: 1200)
scrollView.backgroundColor = .white
view.addSubview(scrollView)
}
func configure1() {
let listButton = UIButton(frame: CGRect(x: view.frame.size.width - 30, y: 57, width: 25, height: 25))
listButton.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
listButton.setImage(UIImage(systemName: "list.bullet"), for: .normal)
scrollView.addSubview(listButton)
}
When calling these functions, only the scrollView is displayed, but the listButton is not displayed
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .gray
configure()
configure1()
}
If combine the code of these functions into one, then everything works. Why doesn't it work out of two functions?
I have one question about your code, you created the ScrollView directly on Storyboard or directly on the code? If you created directly on code, the steps below should work.
I just paste your code to my Xcode and found some problems.
You did not paste the buttonAction code, so this error appeared:
Cannot find 'buttonAction' in scope
To solve this, I did a simple function that prints in console "buttonPrint".
#objc func buttonAction() {
print("buttonPrint")
}
configure1 function did not find scrollView in scope
To solve this, I created a view controller instance property declaring scrollView at the top of my ViewController, outside other functions, using the code below:
var scrollView = UIScrollView()
Redeclaration of scrollView
You declared scrollView on the first line of configure(). To solve the redeclaration, I simply deleted let statement.
Then I builded the app and it worked with these two functions :)
I'm using Xcode version 13.2.1 and an iPhone 11 simulator.
Related
I'v added two UIViewControllers inside of a UIScrollView, and now I'm trying to add targets to the button inside of the UIViewControllers, I've checked the internet but none of those solutions worked for me, this is what I'm doing
inside of the main ViewController
scrollView.contentSize = CGSize(width: view.frame.width * 2, height:0)
let postsViewController = PostsViewController()
postsViewController.scrollView = scrollView
postsViewController.view.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: view.frame.height)
let chatsViewController = ChatsViewController()
chatsViewController.scrollView = scrollView
chatsViewController.view.frame = CGRect(x: view.frame.width, y: 0, width: view.frame.width, height: view.frame.height)
scrollView.addSubview(postsViewController.view)
scrollView.addSubview(chatsViewController.view)
postsViewController.didMove(toParent: self)
chatsViewController.didMove(toParent: self)
then inside the postsViewController I'm doing this
let a = UIButton()
a.setImage(currentTheme.chat, for: .normal)
a.imageEdgeInsets = UIEdgeInsets(top: 1.5, left: 0.0, bottom: 1.5, right: 0.0)
view.addSubview(a)
view.addConstraint("V:|-200-[v0(80)]-0-|",a)
view.addConstraint("H:|-200-[v0(80)]-0-|",a)
a.addTarget(self, action: #selector(abc), for: .touchUpInside)
the add "addConstraint" is an extension function just to add constraint to views
the abc function
#objc func abc() {
print("hello")
}
you can see the scrollView setting here
https://ibb.co/ZB1LJsy
Try to initialize your button like this
let a : UIButton = UIButton(type: .custom)
And add this after to the initialize of your UIScrollView
scrollView.delaysContentTouches = false
I tested in an xcode project. I can not execute the function (button) in the other UIViewController.
By cons if you sent the #selector it works.
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let scrollView : UIScrollView = UIScrollView(frame: CGRect(x: 0, y: 0, width: view.frame.width, height: view.frame.width))
scrollView.delaysContentTouches = false
view.addSubview(scrollView)
let TestViewController = TestViewController1()
TestViewController.select = #selector(demande(sender:))
TestViewController.view.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: view.frame.height)
scrollView.addSubview(TestViewController.view)
print("dd")
}
#objc func demande(sender: UIButton!) {
print("button click", sender.tag)
}
}
class TestViewController1: UIViewController {
var select : Selector!
override func viewDidLoad() {
super.viewDidLoad()
let button : UIButton = UIButton(type: .custom)
button.setImage(#imageLiteral(resourceName: "turn_off_on_power-512"), for: .normal)
button.tag = 2
button.addTarget(self, action: select, for: .touchUpInside)
button.frame = CGRect(x: 0, y: 0, width: 200, height: 200)
view.addSubview(button)
}
}
Hoping that it helps you
okay, I solved my problem, the reason why the button wasn't getting trigger is because I forgot to attach the viewController of the view to the scrollview, so all I had to do is to attach the viewController as well and it worked.
self.addChild(postsViewController)
self.addChild(chatsViewController)
now it is working.
I am a beginner to Swift. I have created a very simple button component, but when I try to add a function that is tied to the button click, I get the error: "Use of unresolved identifier 'self.'" Here is my code:
import UIKit
import PlaygroundSupport
let containerView = UIView (frame: CGRect(x: 0, y: 0, width: 600, height: 600))
containerView.backgroundColor = UIColor.blue
let button = UIButton (frame: CGRect(x: 100, y: 100, width: 200, height: 200))
button.backgroundColor = UIColor.yellow
button.setTitle("Test Button", for: .normal)
button.setTitleColor(.red, for: .normal)
button.layer.cornerRadius = 10
func ButtonClicked(_ sender: UIButton) {
print()
}
button.addTarget(self, action: #selector(ButtonClicked), for: .touchUpInside)
containerView.addSubview(button)
PlaygroundPage.current.liveView = containerView
Any help would be appreciated greatly, thanks!
As mentioned in the comments, there is no self to reference in your code as you're not working within a class or struct. The first thing you'll want to do is to wrap this stuff up into a class, and reorganize it a bit. I'd suggest going back to Xcode and doing the following:
File > New > Playground…
Select iOS > Single View
Save this new Playground somewhere.
Now, you should have a simple Playground created from a template. Here, you'll be able to see how a new class called MyViewController that inherits from UIViewController is created. Within that, you'll find a function called loadView(). Then, outside the class, you'll see where this new view controller is created and placed into the PlaygroundPage's current liveView.
Ideally, you should be able to click the Play button at the bottom of the code view, and if the Assistant editor is open, you'll see a simple view with a "Hello World!" label in the center of the view. (The assistant button is the one with the two interlocking circles.)
Assuming that runs and works for you, then I'd suggest you start adding your code back in and try to follow the patterns setup in the template. Place your code that sets up the view in the loadView() function, and move the ButtonClicked() function outside of that, but still within the class.
With a few modifications, you should be able to get up and running, and start playing around with Swift. As an example, here's a working version of your Playground code:
//: A UIKit based Playground for presenting user interface
import UIKit
import PlaygroundSupport
class MyViewController : UIViewController {
override func loadView() {
let containerView = UIView (frame: CGRect(x: 0, y: 0, width: 600, height: 600))
containerView.backgroundColor = UIColor.blue
let button = UIButton (frame: CGRect(x: 100, y: 100, width: 200, height: 200))
button.backgroundColor = UIColor.yellow
button.setTitle("Test Button", for: .normal)
button.setTitleColor(.red, for: .normal)
button.layer.cornerRadius = 10
button.addTarget(self, action: #selector(ButtonClicked), for: .touchUpInside)
containerView.addSubview(button)
self.view = containerView
}
#objc func ButtonClicked(_ sender: UIButton) {
print("ButtonClicked")
}
}
PlaygroundPage.current.liveView = MyViewController()
Have fun, spend some quality time with the Swift Programming Guide, and things'll start to make sense soon enough.
I am writing my first Swift app for quiz. Each question has random way to render on screen as below screenshot.
I am programming app without story board ie. programmatically. I want to create simple pagination flow for each question within single viewcontroller without using Collocationview, Tableview or navigation.
What I did so far? I have simple viewcontroller with UIView() added as subview. I am adding question components dynamically. Now once user click on continue I wanted remove subview and add new subview with new question. I am able to remove subview but contents on subview seems to be still there as I can see its overwriting.
To get more clarification please view my code.
import UIKit
class QuizController: UIViewController {
let subView = UIView()
var currentQuestion:Int = 1;
let questions = ["This is question 1", "Hello to question 2", "Question 3"]
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = .white
setup_layout()
}
func setup_layout(){
let closeBtn = UIButton(frame: CGRect(x: 5, y: 10, width: 200, height: 50))
closeBtn.backgroundColor = UIColor.red
closeBtn.setTitle("Close", for: .normal)
closeBtn.addTarget(self, action: #selector(close), for: .touchUpInside)
view.addSubview(closeBtn)
//dynamic view
create_subview()
}
func nextQuestion(){
print("Show next")
if let viewWithTag = self.view.viewWithTag(currentQuestion) {
viewWithTag.removeFromSuperview()
currentQuestion += 1;
create_subview()
} else {
print("No!")
}
}
func create_subview(){
let heightOfView = view.frame.size.height
let widthOfView = view.frame.size.width
subView.tag = currentQuestion
subView.backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.2)
subView.frame = CGRect(x: 0, y: 60, width: self.view.frame.width, height: heightOfView - 60)
self.view.addSubview(subView)
let txtLabel1 = UILabel(frame: CGRect(x: 35, y: 120, width: widthOfView , height: 20))
txtLabel1.text = questions[currentQuestion-1]
txtLabel1.font = txtLabel1.font.withSize(12)
subView.addSubview(txtLabel1)
let nextBtn = UIButton(frame: CGRect(x: 5, y: 300, width: 200, height: 50))
nextBtn.backgroundColor = UIColor.red
nextBtn.setTitle("Continue", for: .normal)
nextBtn.addTarget(self, action: #selector(nextQuestion), for: .touchUpInside)
subView.addSubview(nextBtn)
}
func close(){
self.dismiss(animated: true, completion: nil)
}
}
And this is what I see which I click continue.
NOTE: Initially thought using Collocation or Table view will be more appropriate as I can set to scroll horizontally for each question and fetch questions using REST API and place to each cell. But I want to present next screen to user only once then click on continue. I guess with collectionview user can move to next screen on swipe.
Just found the answer. I assumed removing subview will also remove all components on subview by itself ie. UILable and UIButton in my case.
I have to remove them separately.
for subview in self.subView.subviews {
subview.removeFromSuperview()
}
Now i can add tag to components and remove like this:
for subview in self.subView.subviews {
if (subview.tag == 1) {
subview.removeFromSuperview()
}
}
Why are you removing superviews and adding again and again??
Simply change UILabel.text :)
Xcode 7.3, iOS 9.3
I am experimenting with playgrounds so as to expand my capacity to use them to do rapid prototyping. I have created a simple view controller which is adding/centering a button to/in its view. I have duplicated the view controller code in a project and in a playground. When I execute the project in the simulator the button is centered. When I execute the playground the button is not centered. Would someone please help me to understand why this is happening?
I have included 3 screenshots:
Xcode Project With View Controller Code
Simulator Showing the Result of Executing the Project
Playground With View Controller Code and Timeline Showing the Execution Result
Per #Paulw11's comment, here is the updated and working view controller code:
class ViewController : UIViewController {
let button = UIButton(type: .System)
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .whiteColor()
view.layer.borderColor = UIColor.redColor().CGColor
view.layer.borderWidth = 2
button.bounds = CGRect(x: 0, y: 0, width: 50, height: 20)
button.backgroundColor = .blueColor()
button.setTitle("Rotate", forState: UIControlState.Normal)
button.addTarget(self, action: #selector(rotate), forControlEvents: UIControlEvents.TouchUpInside)
view.addSubview(button)
print("View: size is \(view.bounds.width) x \(view.bounds.height), centered #\(view.center)")
print("Button: size is \(button.bounds.width) x \(button.bounds.height), centered #\(button.center)")
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
button.center = view.convertPoint(view.center, fromView: view.superview)
}
func rotate() {
print("Rotate")
}
}
I'm currently following this Apple's iOS Swift tutorial for beginner which explain how to create the rating star view and I don't understand something about the layout code. To layout the rating buttons they created from code using a for-in loop, they create another method:
override func layoutSubviews() {
var buttonFrame = CGRect(x: 0, y: 0, width: 44, height: 44)
// Offset each button's origin by the length of the button plus spacing.
for (index, button) in ratingButtons.enumerate() {
buttonFrame.origin.x = CGFloat(index * (44 + 5))
button.frame = buttonFrame
}
}
But in at the initialisation of buttons :
for _ in 0..<5 {
let button = UIButton(frame: CGRect(x: 0, y: 0, width: 44, height: 44))
button.backgroundColor = UIColor.redColor()
// Add event listener
button.addTarget(self, action: #selector(RatingControl.rateButtonTapped(_:)), forControlEvents: .TouchDown)
ratingButtons += [button]
addSubview(button)
}
Can't they simply adapt the code and, instead of using a wildcard ( _ ) in the for-in loop, use an index and do something like this ? :
for i in 0..<5 {
let button = UIButton(frame: CGRect(x: (i * 49), y: 0, width: 44, height: 44))
button.backgroundColor = UIColor.redColor()
// Add event listener
button.addTarget(self, action: #selector(RatingControl.rateButtonTapped(_:)), forControlEvents: .TouchDown)
ratingButtons += [button]
addSubview(button)
}
I am learning Swift and I don't understand why they override the layoutSubviews method instead of doing that at initialisation. Tell me which way is the cleaner please. Here is the tutorial link: https://developer.apple.com/library/ios/referencelibrary/GettingStarted/DevelopiOSAppsSwift/Lesson5.html
Regards,
The issue is that the frame for the containing rect is not known before the viewWillLayoutSubviews method is called. The layoutSubviews method is called after this method, and each time layoutIfNeeded is called for the view.
This means that the layout can be adapted to updates in the view and only called from one method.
Whether one method is cleaner than another is subjective, and really up to each individual to determine, but at least you should write your UI code in a way that follows the lifecycle of the view.