I'm attempting to return two float values, width and height using a standard UIButton function. I added the "-> Float" but I am greeted with this error. If I change it to "-> Void" then I am left with Unexpected non-void return value in void function. What am i doing wrong here?
#IBOutlet weak var buttonValue: UIButton!
#IBAction func smallB(_ sender: Any) -> Float {
var inital = 2
let divisble = 2
var width: Float
var height: Float
if inital % divisble == 0
{
width = 0.14
height = 0.07
buttonValue.titleLabel?.text = "Big"
inital + 1
}
else
{
width = 0.28
height = 0.14
buttonValue.titleLabel?.text = "Small"
inital + 1
}
return width
return height
}
IBAction function can be invoked/called by user interaction on any UIElements like Button, Switch, Segment Controller. And its response (return type) goes back to user interface element, you may not get float values in your code, if your use IBAction and attach that action to your Interface Builder
Remove return type from your IBAction method, if you've connected method with your Interface Builder Button element. It would be meaningless for you to return value, if your action is generated from IBOutlet element.
#IBAction func smallB(_ sender: Any) {
// action contents
// also remove following return statements
// return width
// return height
}
But if you really want to return height & width (two float values) as response to this function call then you should call it programmatically, and change function declaration like:
func smallBTest(_ sender: Any) -> CGSize {
// action contents
//Update your return statement like
return CGSize(width: width, height: height)
}
let size: CGSize = smallBTest(<button instance > buttonValue)
print("Size: Width = \(size.width) -- Height = \(size.Height)")
// or you can try this as well
#IBAction func smallB(_ sender: Any) {
let size: CGSize = smallBTest(sender)
print("Size: Width = \(size.width) -- Height = \(size.Height)")
}
You cannot return value from IBAction method. So change your method as
#IBAction func smallB(_ sender: Any) {
// your logic
}
And remove return width, return height .
If you want to use width and height make them as global variables.
Related
hi I have an extension in the following code but it could not run for me :(
Actually I want to scroll my textView with a entered speed but I don't know how can I do this
extension UITextView {
func simple_scrollToBottom() {
let textCount: Int = text.count
guard textCount >= 1 else { return }
scrollRangeToVisible(NSRange(location: textCount - 1, length: 1))
}
}
I used it as self.akor_goster.simple_scrollToBottom() in viewdidload
First, you would not want to call this from viewDidLoad()...
At that point, auto-layout has not finished arranging everything in the view, frame and text view content is not yet ready.
Also, you don't want to start an animation yet, because the view has not yet appeared.
So, if you want this to happen automatically, put the call in viewDidAppear().
Second, you cannot control the scrolling speed when calling scrollRangeToVisible() -- but you can calculate the offset and animate that change with your desired speed.
Try this extension:
extension UITextView {
func timedScrollToBottom(duration d: Double) {
let y = contentSize.height - frame.size.height
// if y is less than zero, all the text fits, so no scrolling needed
guard y > 0 else {
return
}
// Duration is in seconds
UIView.animate(withDuration: d, animations: {
self.contentOffset.y = y
})
}
}
and call it like this:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.akor_goster.timedScrollToBottom(duration: 2.0)
}
Note:
I am looking for alternate solution, Since I have tried changing lots of Sprite Kit properties values, and came upto conclusion that it is not possible with those changes. So I ain't posting any code. You can try the mentioned library for more information.
Original Question:
I am using BLBubbleFilters library to create Bubbles.
This library used magnetic property to pull align items to the centre of the screen. And each time the screen opens the positioning of each node is different.
What have I tried so far?
I have tried playing around with the library and its physics attributes(e.g. Magnetic property) to achieve my goal.
The goal is:
Place 'Family' node over and above all nodes, and rest of the nodes below it.
What I thought is to that, each node will have a property for example "priority(Integer)". That is, the Priority 0 nodes will be placed above all nodes, priority 1 nodes below 0 priority nodes and so on.
Since SpriteKit deals with Physics properties, is there is any such property or way to achieve my goal?
See, how the family bubble is at the Top.
I had to do something like that and I did By creating a custom SelectInterestsCollectionViewFlowLayOut and I added the bubbles in the UICollectionViewCells.
The result was this.
Every cell is a white square and inside I have an image view with corner radius 1/2 * height.
The difference is that in my case I had the same bubble size for all my bubbles.
My code is:
enum InterestBubbleType {
case A
case B
case C
case D
case E
func getYPosition(cellSideSize: CGFloat) -> CGFloat {
let spaceFromTopForTwoCellsOnTop: CGFloat = cellSideSize / 2
switch self {
case .A:
return cellSideSize
case .B:
return cellSideSize * 2
case .C:
return spaceFromTopForTwoCellsOnTop
case .D:
return cellSideSize + spaceFromTopForTwoCellsOnTop
case .E:
return 0
}
}
}
class SelectInterestsCollectionViewFlowLayOut: UICollectionViewFlowLayout {
var cache: [UICollectionViewLayoutAttributes] = []
var layoutAttributes: [UICollectionViewLayoutAttributes] = []
var cellSideSize: CGFloat!
var xPosition: CGFloat = 0
var itemPositions: [InterestBubbleType] = [.A,.C,.D,.E,.A,.B,.C,.D,.E,.A,.B,.C,.D,.A,.B,.C,.D,.E,.A,.B]
var positionsToIncreaseXPosition = [0,2,5,7,10,12,14,16]
override func prepare() {
cache = []
super.prepare()
//Prepare constants
self.xPosition = 0
cellSideSize = self.collectionView!.height() / 3
self.configureCasheForTwentyInterests()
}
private func configureCasheForTwentyInterests() {
for i in 0...(self.itemPositions.count - 1) {
let indexPath = IndexPath(item: i, section: 0)
let xPosition = self.xPosition
let frame = CGRect(x: xPosition, y: self.itemPositions[i].getYPosition(cellSideSize: self.cellSideSize), width: self.cellSideSize, height: self.cellSideSize)
let atribute = UICollectionViewLayoutAttributes(forCellWith: indexPath)
atribute.frame = frame
cache.append(atribute)
if self.positionsToIncreaseXPosition.contains(i) {
self.xPosition += self.cellSideSize
}
}
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
self.layoutAttributes = []
for uiCollectionViewLayoutAttribute in self.cache {
self.layoutAttributes.append(uiCollectionViewLayoutAttribute)
}
return layoutAttributes
}
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
return layoutAttributes[indexPath.row]
}
override var collectionViewContentSize: CGSize {
let totalWidth = cellSideSize * 9
return CGSize(width: totalWidth, height: self.collectionView!.height())
}
}
Now how it works is that in my collection view there are 5 different y positions that a bubble can be inside the collection view and they are A,B,C,D,E and I also keep in mind when you need to move 1 more x position which is 1 * collectionViewWidth size.
You can configure the code to achive your own result.
In my case I had a fixed number of bubbles and a fixed view to show them. The reason I chose to do it with collection view was because if the number change you can just add positions to change x position in positionsToIncreaseXPosition and it will work.
While debugging the code in this question UILabel doesn't update after button clickl I noticed that the simulated memory usage kept growing, even when I was in the debugger and not viewing the app.
I modified the addButtonsAndLabels() function to this to prevent memory loss, is this a bad practice?
func addButtonAndLabels() -> Void {
// If the width of the screen hasn't been used as a base for the size of the sub-views then
// this function is not ready to generate the sub-views.
if (selfWidth < 1.0) {
return;
}
var viewElementVerticalLocation: CGFloat = startingVerticalLocation;
// To prevent memory leaks only create the UIView object if it hasn't already been created
if (self.getGPSLongitudeAndLatitudeWithTimeStamp == nil) {
self.getGPSLongitudeAndLatitudeWithTimeStamp = makeAButton(yButtonStart: viewElementVerticalLocation, buttonTitle: "Get GPS Location with TimeStamp", underSubview: nil)
self.getGPSLongitudeAndLatitudeWithTimeStamp?.addTarget(self, action: #selector(setLabelWithGPSLatitudeAndLongitudeWithTimeStampData), for: .touchUpInside)
viewElementVerticalLocation += buttonVerticalSeparation
}
// ... more of the above ...
}
Is this code interfering with ARC? Is this code unnecessary?
I also noticed that viewDidLoad() was called before init(), since I had breakpoints in both functions.
If this is a duplicate please point me in the correct direction.
EDIT
import UIKit
class ViewController: UIViewController {
var getGPSLongitudeAndLatitudeWithTimeStamp : UIButton?
var getBatteryLevelAndState : UIButton?
var getNextorkImplementation : UIButton?
var displayButtonAction : UILabel?
var displayDataModel : PCI7DataModelLibrary?
// The following variables are used in multiple functions. They are constant during the display of the super view
// and control the size of the subviews. They should change when the orientation changes
var selfWidth : CGFloat = 0.0
var buttonHeight : CGFloat = 0.0
var viewElementWidth : CGFloat = 0.0
var buttonYCenterOffset : CGFloat = 0.0 // The y center should be half the value of the height
var buttonXCenter : CGFloat = 0.0 // Center the button title relative to the width of the button and the width of the super view
var buttonXInit : CGFloat = 0.0
var buttonVerticalSeparation : CGFloat = 0.0
var startingVerticalLocation : CGFloat = 0.0
var displayLabelHeight: CGFloat = 75.0
// TODO: This function should be altered so that all values are calculated on screen height and screen width,
// this will allow for changes in orientation.
func initFramingValuesOfMyDisplay() {
selfWidth = self.view.bounds.size.width
buttonHeight = 20.0 // This should be programmable in relative to self.view.bounds.size.height
viewElementWidth = 0.8 * selfWidth;
buttonXCenter = selfWidth / 2.0; // Center the button title relative to the width of the button and the width of the super view
buttonXInit = selfWidth * 0.1; // 10 percent margin on the left leaves a 10% margin on the right as well
buttonYCenterOffset = buttonHeight / 2.0; // The y center should be half the value of the height
buttonVerticalSeparation = buttonHeight + buttonYCenterOffset;
startingVerticalLocation = 430.0; // 430 was chosen based on experimentation in the simulator
}
// This function is called when the getGPSLongitudeAndLatitudeWithTimeStamp button is receives the touchUpInside event.
func setLabelWithGPSLatitudeAndLongitudeWithTimeStampData()
{
var actionString : String = "Testing Label Text"
if (self.displayDataModel != nil) {
actionString = (self.displayDataModel?.provideGPSLocationData())!
}
else {
actionString = "GPS Button Action Failure: Data Model not created"
}
DispatchQueue.main.async {
self.displayButtonAction?.text = nil
self.displayButtonAction?.text = actionString
}
}
// This function is called when the getBatteryLevelAndState button is receives the touchUpInside event.
func setLabelWithBatteryLevelAndState() {
var actionString : String = "Get Battery Level and State";
if (self.displayDataModel != nil) {
actionString = (self.displayDataModel?.provideBatteryLevelAndState())!
}
else {
actionString = "Battery Button Action Failure: Data Model not created"
}
DispatchQueue.main.async {
self.displayButtonAction?.text = nil
self.displayButtonAction?.text = actionString
}
}
// This function is called when the getNextorkImplementation button is receives the touchUpInside event.
func setLabelActionNetwork() {
var actionString :String = "Fake Button set to American Express Stock Price"
if (self.displayDataModel != nil) {
actionString = (self.displayDataModel?.provideNetworkAccessData())!
}
else {
actionString = "Network Button Action Failure: Data Model not created"
}
DispatchQueue.main.async {
self.displayButtonAction?.text = nil
self.displayButtonAction?.text = actionString
}
}
func makeAButton(yButtonStart : CGFloat, buttonTitle: String, underSubview: UIButton?) -> UIButton
{
let thisButton = UIButton.init(type: .system)
thisButton.frame = CGRect(x: buttonXInit, y: yButtonStart, width: viewElementWidth, height: buttonHeight)
thisButton.setTitle(buttonTitle, for:UIControlState.normal)
thisButton.backgroundColor = UIColor.yellow
thisButton.setTitleColor(UIColor.black, for: UIControlState.normal)
if ((underSubview) == nil) {
self.view.addSubview(thisButton)
}
else {
self.view.insertSubview(thisButton, belowSubview:underSubview!)
}
return thisButton;
}
func makeALabel(yLabelStart : CGFloat, height: CGFloat, underSubview: UIButton?) -> UILabel
{
let thisLabel = UILabel.init()
thisLabel.frame = CGRect(x: buttonXInit, y: yLabelStart, width: viewElementWidth, height: height)
thisLabel.font = thisLabel.font.withSize(12) // Reduce the size of the text so that more output fits on a single line
thisLabel.lineBreakMode = .byWordWrapping;
thisLabel.numberOfLines = 0; // Allow the label to grow as necessary
thisLabel.textAlignment = NSTextAlignment.center;
thisLabel.textColor = UIColor.black;
if ((underSubview) == nil) {
self.view.addSubview(thisLabel)
}
else {
self.view.insertSubview(thisLabel, belowSubview:underSubview!)
}
return thisLabel;
}
override func viewDidLoad() {
super.viewDidLoad()
// rather than assume a particular background color, set the background color so that everything can be seen.
self.view.backgroundColor = UIColor.white
initFramingValuesOfMyDisplay()
if (self.displayDataModel == nil) {
self.displayDataModel = PCI7DataModelLibrary.init()
}
addButtonAndLabels()
}
func addButtonAndLabels() -> Void {
// If the width of the screen hasn't been used as a base for the size of the sub-views then
// this function is not ready to generate the sub-views.
if (selfWidth < 1.0) {
return;
}
var viewElementVerticalLocation: CGFloat = startingVerticalLocation;
// To prevent memory leaks only create the UIView object if it hasn't already been created
if (self.getGPSLongitudeAndLatitudeWithTimeStamp == nil) {
self.getGPSLongitudeAndLatitudeWithTimeStamp = makeAButton(yButtonStart: viewElementVerticalLocation, buttonTitle: "Get GPS Location with TimeStamp", underSubview: nil)
self.getGPSLongitudeAndLatitudeWithTimeStamp?.addTarget(self, action: #selector(setLabelWithGPSLatitudeAndLongitudeWithTimeStampData), for: .touchUpInside)
viewElementVerticalLocation += buttonVerticalSeparation
}
if (self.getGPSLongitudeAndLatitudeWithTimeStamp == nil) {
self.getBatteryLevelAndState = makeAButton(yButtonStart: viewElementVerticalLocation, buttonTitle: "Get Battery Level and State", underSubview: getGPSLongitudeAndLatitudeWithTimeStamp)
self.getBatteryLevelAndState?.addTarget(self, action: #selector(setLabelWithBatteryLevelAndState), for: .touchUpInside)
viewElementVerticalLocation += buttonVerticalSeparation
}
if (self.getNextorkImplementation == nil) {
self.getNextorkImplementation = makeAButton(yButtonStart: viewElementVerticalLocation, buttonTitle: "Get American Express Stock Price", underSubview: getBatteryLevelAndState)
self.getNextorkImplementation?.addTarget(self, action: #selector(setLabelActionNetwork), for: .touchUpInside)
viewElementVerticalLocation += buttonVerticalSeparation
}
if (self.displayButtonAction == nil) {
self.displayButtonAction = makeALabel(yLabelStart: viewElementVerticalLocation, height: displayLabelHeight, underSubview: getNextorkImplementation)
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
required init(coder aDecoder: NSCoder) {
super.init(nibName: nil, bundle: nil)
if (self.displayDataModel == nil) {
self.displayDataModel = PCI7DataModelLibrary.init()
}
initFramingValuesOfMyDisplay()
}
}
It's a little difficult to answer this question because I fear you have a lot of misunderstandings about how Swift memory management works and how iOS view controller life cycles work. They're not surprising misunderstandings; it's just hard to know where to start here. I may cover things you're already aware of.
First, start with studying the Automatic Reference Counting section of the Swift manual. The key to understanding ARC is that it is based on strong references. If two things have strong references to each other, that's a retain loop and until it's broken, neither object will be deallocated.
It isn't really meaningful to ask "is Swift by default autorelease." Autorelease is a slightly more advanced concept that you probably don't have to understand for basic Swift. (I've worked with it so long that I hope I'm not being overly dismissive of whether people need to understand it, but I think you can avoid thinking about it for now.) Autorelease has to do with objects that you want to exist for the rest of the current event loop, but would otherwise have a zero retain count. While it still exists in ARC, it's mostly for manual retain counting, and you rarely need to think too much about it in Swift, particularly as a beginner.
I also noticed that viewDidLoad() was called before init(), since I had breakpoints in both functions.
It's not possible for viewDidLoad to be called before init on a given object. Your problem, almost certainly, is that there are more than one of this object, and that's surprising you. Generally if you print(self) (or p self in the debugger) it will show you the address of the object. If it has a different address, it's a different object. You may want to implement deinit so you can put a breakpoint there as well and explore the life cycle of the view controller.
I assume somewhere there's an addSubview call on this button. What's almost certainly happening is that you're calling addButtonAndLabels multiple times (perhaps because you are confused about view lifecycle), and each call adds the button to the view. So you get more and more buttons in the view, and each takes memory. You need to make sure you only add things once if you only want one. You can see this problem with p view.subviews in the debugger (or print(view.subviews)). You'll probably see a ton of buttons. This isn't a leak. These are just objects you're creating that you didn't mean to create.
I'm writing an app in which there's a scrollview and what I want is to set the button's alpha to 0 at first and change it into 1 when the user scrolls to the last page. I'm writing code like this:
#IBAction func startButton(sender: UIButton) {
sender.layer.cornerRadius = 2.0
sender.alpha = 0.0
}
extension UserGuideViewController: UIScrollViewDelegate {
func scrollViewDidScroll(scrollView: UIScrollView) {
let offset = scrollView.contentOffset
pageControl.currentPage = Int(offset.x / view.bounds.width)
if pageControl.currentPage == numberOfPages - 1 {
UIView.animateWithDuration(0.5) {
self.startButton.alpha = 1.0
}
} else {
UIView.animateWithDuration(0.2) {
self.startButton.alpha = 0.0
}
}
}
}
and it says the Value of type (UIButton)->() has no member alpha. I don't know how to do this.
I know it would be easy if this button is an #IBOutlet but I need to set it as an #IBAction so that when it gets touched I can show another view controller.
You need to create both an #IBOutlet and an #IBAction for the button. Drag twice from Interface Builder to your view controller and select the appropriate values (outlet or action) from the popup menu and give them different names. Then access the outlet in your scrollViewDidScroll method.
I have multiple custom pop up views that I call (to set it up, animate the pop up and animate the dismiss) but I want to have one variable that I can replace in the multiple functions that can be set by a switch statement with one variable. So I don't have 4+ of every function with the same code but different uiView names. How is that done please?
var myPopupView:NotePopUpView!
var myInformationPopUpView:InformationPopUpView!
var myDatePopUpView:DatePopUpView!
var myPainDiagramView:PainDiagramPopUpView!
func notePopUpCalled() {
if (myPopupView != nil) {
self.myPopupView.view.removeFromSuperview()
}
// making different size pop up for different screens
var popUpWidth = GlobalConstants.ScreenStats.screenWidth - 60
if UIScreen.mainScreen().bounds.size.width > 320 {
if UIScreen.mainScreen().scale == 3 {
popUpWidth = 320
} else {
popUpWidth = 320
}
} else {
popUpWidth = 283
}
self.myPopupView = NotePopUpView(frame: CGRect(x: 20, y: 350, width: popUpWidth, height: 334))
myPopupView.center = self.view.center
self.view.addSubview(myPopupView)
showAnimate()
}
Generally when it is necessary to apply one method on different types recommended to use generics.
func setupView<T: UIView>(view: T) {
//Make all setups
}
Also you can use as? operator to downcasting your view types to UIView.
Hope it helps.