I have got an UIButton on a storyboard ViewController. When I load data into the form and the layout is significantly changing the button does not recognise the touch action.
I have figured out that when button is visible on the scrollview right after it if filled with data, the touch action works.
If the data too long and the button is not visible at first, just when it is scrolled into the display, the touch action does not work.
I was checking if something is above the button, but nothing. I have tried to change the zPosition of the button, not solved the problem.
What can be the issue?
I have made custom classes from the UIScrollView and the UIButton to check how the touches event triggered. It is showing the same behaviour, which is obvious. If the button is visible right at the beginning, the UIButton's touchesBegan event is triggered. If the button moves down and not visible at the beginning, it is never triggered, but the scrollview's touchesBegan is called instead.
Depending on the size of the data I load into the page sometimes the button is visible at the beginning, but the form can be still scrolled a bit. In this case the button still work, so it seems that this behaviour is not depending on if the scrollview is scrolled before or not, just on the initial visibility of the button.
Is there any layout or display refresh function which should be called to set back the behaviour to the button?
The code portion which ensures that the contentview is resized for the scroll if the filled data requires bigger space.
func fillFormWithData() {
dispDescription.text = jSonData[0]["advdescription"]
dispLongDescription.text = jSonData[0]["advlongdesc"]
priceandcurrency.text = jSonData[0]["advprice"]! + " " + jSonData[0]["advpricecur"]!
validitydate.text = jSonData[0]["advdate"]!
contentview.layoutIfNeeded()
let contentRect = CGRect(x: 0, y: 0, width: scrollview.frame.width, height: uzenetbutton.frame.origin.y+uzenetbutton.frame.height+50)
contentview.frame.size.height = contentRect.size.height
scrollview.contentSize = contentview.bounds.size
}
Ok, so another update. I have coloured the contentview background to blue and the scrollview background to white. When I load the data and resize the layout constraints, the contentview is resizing as expected, however now the scrollview is going to the bottom. After I scroll the view it is resizing to the original size which fits the screen. Now the button is only recognised when I touch the are which is blue behind. With the white background it is not recognised anymore, so it seems that the scrollview is hiding the button.
Let me get this clear the button is added in storyboard and it is a spritekit project?? If you are using zPosition?? Why don’t u connect the UIButton via the assistant editor as an IBAction then the action is always tied to the button.
You can also do it differently
Create an SKLabelNode and put it on the screen where you want to have the button and then set a name to it as myButton
override func touchesBegan(_ touches: Set<UITouch>, with event:
UIEvent?) {
if let touch = touches.first {
let location = touch.location(in: self)
let tappedNodes = nodes(at: location)
for node in tappedNodes {
if node.name == "myButton" {
// call your action here
}
}
}
}
EDIT 1:
You could also try auto resizing your scrollView.content this works also if you are adding any views via the app or programmatically
private func resizeScrollView(){
print("RESIZING THE SCROLLVIEW from \(scrollView.contentSize)")
for view in scrollView.subviews {
contentRect = contentRect.union(view.frame)
}
scrollView.contentSize = CGSize(width: contentRect.size.width, height: contentRect.size.height + 150)
print("THE CONTENT SIZE AFTER RESIZING IS: \(scrollView.contentSize)")
}
EDIT 2: I think I found the issue with your project. You need to move the MessageButton(UzenetButton) above DispDescription label in the object inspector in that way it will always be above your message textView.
At the moment the UzeneButton is at the very far back in your view hierarchy so if your textView is resizing whilst editing it covers the button that is why you cannot click on it.
See #Endre Olah,
To make situation more clear do one more thing, set clipToBound property of contentview to true.
you will notice that after loading of data your button not fully visible, it means it is shifting out of bound of its parentView (ContentView)
And that's why button is not taking your touch. However, if you carefully touch upper part of button it still do its job. Because upper part is still in bound of ContentView
Solution :
After loading of data you have to make sure that you increase height of ContentView such that button should never go out of bound of its parentView(ContentView).
FOR EXAMPLE
#IBOutlet var heightConstraintOfContentView : NSLayoutConstraint!
After loading of data
let contentRect = CGRect(x: 0, y: 0, width: scrollview.frame.width, height: uzenetbutton.frame.origin.y+uzenetbutton.frame.height+50)
heightConstraintOfContentView.constant = contentRect.size.height
contentView.layoutIfNeeded()
I use following steps when I need to use scrollview with dynamic content:
1) Firstly add a scrollView with top, bottom, trailing and leading is 0 to super view.
2) Add a view to scrollView and view's trailing, leading bottom and top space to scrollView can be set to 0 (or you can add margin optionally).
3) Now, you should add UI elements like buttons, labels with appropriate top, bottom, trailing and leading margins to each other.
4) Lastly, add equal height and equal width constraint to view with Safe Area:
and change equal height priority of view to 250:
It should solve your problem with UIScrollView.
Finally, I have found the solution in another chain, once it became clear that the scrollview's contentview is resizing on scroll event to the original size. (Not clear why this is like this, but that is the fact.)
So I had to add a height constraint to the contentview in the storyboard and create an outlet to it and adjust this constraint when the content size is changing, like this:
#IBOutlet weak var ContentViewHeight: NSLayoutConstraint!
func fillFormWithData() {
dispDescription.text = jSonData[0]["advdescription"]
dispLongDescription.text = jSonData[0]["advlongdesc"]
priceandcurrency.text = jSonData[0]["advprice"]! + " " + jSonData[0]["advpricecur"]!
validitydate.text = jSonData[0]["advdate"]!
contentview.layoutIfNeeded()
let contentRect = CGRect(x: 0, y: 0, width: scrollview.frame.width, height: uzenetbutton.frame.origin.y+uzenetbutton.frame.height+50)
contentview.bounds = contentRect
scrollview.contentSize = contentRect.size
----------- This is the key line to the success ----------
ContentViewHeight.constant = contentRect.size.height
----------------------------------------------------------
}
After this is added, it works perfectly.
I have a swipe gesture in my app that when you swipe, it changes the text on screen. How would I make it so it looks like it is sliding to the new text rather than just instantly changing the text?
You could do one thing!!!add a view behind your gesture view ,that view should look same as that of gesture View(should even have same text),and once after recognizing gesture bring that view in-front of your gesture view it should have old text(don't update the text) and update your gesture view's text ones it moves behind newly added view, just change the frame of newly added view so that it gives a sliding kind of effect(change its width) once after the completing animation bring that view back to gesture view and change its frame to previous its previous value.
check this sample:
func handleGesture(sender:UISwipeGestureRecognizer) {
//bring background view to front
self.view.bringSubviewToFront(self.backGroundView)
self.gestureView.userInteractionEnabled = false
//capture its initialAutolayout constraint
let backgroundViewSpaceConstarint = self.backGroundViewLeftSpaceConstraint.constant
UIView.animateWithDuration(2, animations: { [unowned self]() -> Void in
self.someRandomValue++
// update text label in gesture view which is behind background View
self.gestureViewTextLabel.text = "swiped \(self.someRandomValue) times"
//slide backgroundView in required direction by using autolayout
self.backGroundViewLeftSpaceConstraint.constant = self.backGroundView.bounds.size.width
self.backGroundView.layoutIfNeeded()
}) { [unowned self](completion:Bool) -> Void in
//after animation complition bring gesture view to the front.
self.backgroundTextLabel.text = "swiped \(self.someRandomValue) times"
self.gestureView.userInteractionEnabled = true
//upadte background view so that it will look the same for next swipe
self.backGroundViewLeftSpaceConstraint.constant = backgroundViewSpaceConstarint
//send the background view behind gesture view
self.view.sendSubviewToBack(self.backGroundView)
}
}
Using the Interface Builder, I created a view with a UIScrollView in it.
I programmaticly add the buttons to the empty UIScrollView.
When the orientation changes, I use
- (void) willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
to call a method that resets the buttons on screen.
After that, the uiScrollView-content gets a new size using setContentSize.
No matter the width of the ContentSize, I can only interact (scroll/or tap a button) on the first 320px of the screen - which is the screen width in portrait mode.
When I set the contentSize-width to 2000, I can scroll to the left, but only with my fingers on the first 320 px instead of the full 480 (using a 3.5 inch iPhone).
What am I missing?
You need to resize the scrollview's frame, not just the content size (in fact, often you don't need to resize the content size, as the content may not change following an orientation change, but just the frame).
I had the same problem with a Popup (UIView) that was called from a (UIViewController).
In the UIViewController the popup was created as followed
func createPopup() {
let myPopup = MyPopup(frame: UIScreen.mainScreen().bounds)
self.view.addSubview(myPopup)
}
In the Popup (UIView) I needed to override layoutSubviews as follows:
override func layoutSubviews() {
super.layoutSubviews()
self.setNeedsDisplay()
let mainScreenBounds = UIScreen.mainScreen().bounds
view.frame = mainScreenBounds
super.frame = mainScreenBounds <-- Need to update the parent
** perform additional rotation/orientation code here **
}
I have an Activity that populates two views on a ViewFlipper. I added an onTouch(View v, MotionEvent event) public boolean method to the Activity. The method is implemented so that when the person taps on the screen the ViewFlipper goes to the next view. It works great but some of the text is too long so I went into my XML file and surrounded the textfield in one of my ViewFlipper views with a linearlayout and then a ScrollView. But now when I'm viewing the fields that are too long and show a scroll bar, I can't display the previous view. The onTouch method in my main activity isn't being executed. I have not been able to figure this out. I've read some posts about implementing or overriding the methods in ScrollView but I don't know where to do this in my activity. Does anyone know how I can program ScrollView to not intercept but keep its ability to scroll the view?
ScrollView myScroll;
myScroll.setOnTouchListener(new OnTouchListener){
#Override
public boolean onTouch(View v, MotionEvent touchevent) {
switch (touchevent.getAction()) {
case MotionEvent.ACTION_DOWN: {
oldTouchValue = touchevent.getX();
break;
}
case MotionEvent.ACTION_UP: {
float currentX = touchevent.getX();
if (oldTouchValue < currentX) {
//left swipe
return true;
}
if (oldTouchValue > currentX) {
//right swipe
return true;
}
return false;
}
}
I have created a view in interface builder that contains another view (content area) and a button bar at the bottom.
The hierarchy is as:
->View
--->mapContainer UIView
----->map MKMapView
----->OverlayView UIView
--->ToolBar UIToolBar
I would like the mapContainer to resize to full window when the ToolBar is hidden.
I would like the map and the OverlayView to resize to the mapContainer size
I have attempted the following code, but it has no effect. Please advise?
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
this.mapContainer.AutosizesSubviews = true ;
this.View.AutosizesSubviews = true ;
try
{
this.mapContainer.AutoresizingMask = UIViewAutoresizing.FlexibleHeight | UIViewAutoresizing.FlexibleWidth ;
this.map.AutoresizingMask = UIViewAutoresizing.FlexibleHeight | UIViewAutoresizing.FlexibleWidth ;
this.map.SizeToFit();
this.mapContainer.SizeToFit();
this.map.SizeToFit();
this.View.Frame = new System.Drawing.RectangleF(0,0,this.View.Frame.Width, this.View.Frame.Height );
this.mapContainer.LayoutSubviews();
}
catch(Exception ex)
{
Console.Write(ex.ToString());
}
}
You could also try to redraw the subviews in an overridden method public override void LayoutSubviews with your class.
There you can reset the frames of all your frames. The method SizeToFit() has a tendency to not work as it's name sounds... So first size to fit, then reset the frame.
Hope it helps :)
Check out this article may be this can help you.Its not related to monotouch but it will give nice concept of how to arrange elements together in IB.
http://programmingobsession.blogspot.com/2009/05/real-world-iphone-rotation-part-1.html