UIScrollView not hiding content - ios

I have a scrollView with a rectangular frame to display a set of subViews and page them, I would expect those other subViews to be hidden and only the current content displayed but instead I get all this overlap onto my screen.
if scrollViewHasBeenSetUp == false {
var index = 0
for event in eventList.events {
let match = getMatchCell(event)
match.frame = CGRect(x: index * Int(matchScrollView.frame.width), y: 0, width: Int(matchScrollView.frame.width), height: Int(matchScrollView.frame.height))
matchScrollView.contentSize = CGSize(width: Int(match.frame.width) * eventList.events.count, height: 120)
matchScrollView.addSubview(match)
index++
print("cell frame: \(match.frame)")
}
scrollViewHasBeenSetUp = true
}
Using the visual inspector you can see the scrollViews frame has been set correctly but its content still isn't being hidden.

Did you select clip subviews?
And I see your y is always 0,is it a vertical scroll view?

Related

Increasing frame.origin.x vs constant

I'm trying to understand how iOS frame and bounds works.
I put an subView:UIView on UIViewController and a button which can increase subView's frame origin coordinate and change textlabel with its value.
like this,
let subView: UIView = UIView(frame: CGRect(x: 50, y: 50, width: 100, height: 100))
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(subView)
subView.backgroundColor = .blue
}
#IBAction func btnMoveBottomView(_ sender: Any) {
subView.frame.origin.y = subView.frame.origin.y + 100
lbFrameInfo.text = String(format:"sub = (%.1f, %.1f)", subView.frame.origin.x, subView.frame.origin.y)
self.view.setNeedsLayout()
self.view.layoutIfNeeded()
}
After I make this action, I see subView go down but text doesn't change.
on debug console I see this change
po subView.frame.origin
▿ (50.0, 2350.0)
- x : 50.0
- y : 2350.0
updateViewConstraints has been called too.
override func updateViewConstraints() {
print("updateViewConstraints")
super.updateViewConstraints()
}
Instead of increasing frame.origin.x like this, increasing value of leading constant works perfectly.
It would be appreciated if someone can guide me the differences of these and the concept of frame and if it is related with auto-layout things
According to your question,
You have added subView to ViewController view so when u create view
let subView: UIView = UIView(frame: CGRect(x: 50, y: 50, width: 100, height: 100))
and do this
view.addSubview(subView)
it means your subview will be at 50 position down and away from left of the screen.
so when u do this.
subView.frame.origin.y = subView.frame.origin.y + 100
it will move your frame further down in the screen as you are changing the y position of frame which is the starting position of creating your view on screen.
At its simplest, a view’s bounds refers to its coordinates relative to its own view (as if the rest of your view hierarchy didn’t exist), whereas its frame refers to its coordinates relative to its parent’s view. Frame will reflect the position in its parents view.
This means a few things:
1.If you create a view at X:0, Y:0, width:100, height:100, its frame and bounds are the same.
2.If you move that view to X:100, its frame will reflect that change but its bounds will not. Remember, the bounds is relative to the view’s own space, and internally to the view nothing has changed.
3.If you transform the view, e.g. rotating it or scaling it up, the frame will change to reflect that, but the bounds still won’t – as far as the view is concerned internally, it hasn’t changed
hope it clears your doubts

only last added view is visible in UIScrollView iOS

I am facing an issue with scrollView. I have added scrollView in my storyboard as
View
- MainView
- ScrollView
- accountSavingSubView
Now I am trying to add views in accountSavingSubView as below
//MARK:- Create and add wallets on the UI
private func createAndAddWallets()
{
var yPosition : CGFloat = 0.0
//1. Create left hand side wallets with help of keys
for key in dictData.keys
{
let walletView:Wallet = Bundle.main.loadNibNamed("Wallet", owner: self, options: nil)?.first as! Wallet
walletView.translatesAutoresizingMaskIntoConstraints = false
walletView.frame = CGRect(x: 0.0, y: yPosition, width: accountSavingSubView.frame.width, height: accountSavingSubView.frame.height)
walletView.accountNameLabel.text = key
accountSavingSubView.addSubview(walletView)
yPosition += walletView.frame.maxY
walletView.isUserInteractionEnabled = true
print("walletView frame is :\(walletView.frame)")
}
//set continer scroll content size
self.contanerScrollView.contentSize = CGSize(width: self.accountSavingSubView.frame.width, height: CGFloat(80.0/320.0)*UIScreen.main.bounds.width*CGFloat(self.dictData.keys.count))
}
Where dictData is a dictionary of ([String: String]).
But in my view, I can see only last view is added on (x=0, y=0). ScrollView content size is correct & I can scroll without having views except last one at first place(x:0,y:0).
Please let me know what am I doing wrong here in my code while adding views.
You need to comment this
walletView.translatesAutoresizingMaskIntoConstraints = false
as it's used with setting constraints only ,and verify that the supplied frame values here are not 0
walletView.frame = CGRect(x: 0.0, y: yPosition, width: accountSavingSubView.frame.width, height: accountSavingSubView.frame.height)
btw you can also comment it as the frame will be captured as you set it in xib if you make it freedom size

UIScrollView didn't scroll

I've some problem with ScrollView.
I'm trying to create a scrollview and add to it dynamically some buttons.
So i create a scrollView in my main storyboard with some constraints. (0 to left, 0 to right, 0 to botton and 1/10 height).
Now, i want to add some button.
#IBOutlet var scrollView: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
for index in 0..<12 {
let frame1 = CGRect(x: 0 + (index * 60), y: 0, width: 45, height: 45 )
let button = UIButton(frame: frame1)
button.setTitle("toto", forState: .Normal)
button.backgroundColor = UIColor.green()
scrollView.addSubview(button)
}
}
So now the problem is : my buttons are present but i can't scroll. The scroll is enabled in the storyBoard. I tried to enabled it in the code but nothing changed..
set the contentSize of your scrollView
self. scrollView.contentSize = CGSizeMake(required_width, required_height)
for example
self. scrollView.contentSize = CGSizeMake(500, 74) // custtomize ur self
You have to set the contentSize of the scrollView
The height to set is probably the origin.y + the size.height of the last button.
use this code to set scrollview height
scrollview.contentInset = UIEdgeInsetsMake(0, 0, 50, 0)
//50 is the height of scroll view you can change it to the height you want
scrollview.bounces = false
or you can also change content inset from here
by changing the values of content inset (height or width)

Image overlaps previous view when tapping/swiping back

I have a UIScrollView which covers one of my views entirely. I have added a background image to this same view which scrolls at a slightly different rate to the actual UIScrollView. This works absolutely fine unless I use the back swipe gesture or tap the 'Back' button. What happens is the image covers the view for about 0.5 seconds before disappearing, and it looks pretty bad.
This is what I mean:
As you can see, that is mid way through the gesture, and rather than being able to see the previous view, you just see the part of the image that is off to the left. It doesn't happen on the first page of the UIScrollView so I guess it's because the image is overlapping the previous view.
Here is my code:
override func viewDidLoad() {
let pagesScrollViewSize = scrollView.frame.size
scrollView.contentSize = CGSize(width: pagesScrollViewSize.width * CGFloat(images.count), height: pagesScrollViewSize.height)
backgroundImageView.frame = CGRect(x: 0, y: 0, width: 2484, height: 736)
backgroundImageView.image = UIImage(named: "SF.png")
var visualEffectView = UIVisualEffectView(effect: UIBlurEffect(style: .Light)) as UIVisualEffectView
visualEffectView.frame = backgroundImageView.bounds
backgroundImageView.addSubview(visualEffectView)
view.addSubview(backgroundImageView)
view.sendSubviewToBack(backgroundImageView)
scrollView.backgroundColor = UIColor.clearColor()
}
func scrollViewDidScroll(scrollView: UIScrollView) {
loadVisiblePages()
var factor = scrollView.contentOffset.x / (scrollView.contentSize.width - 414);
if factor < 0 {
factor = 0
}
if factor > 1 {
factor = 1
}
var frame: CGRect = backgroundImageView.frame
frame.origin.x = factor * (414 - backgroundImageView.frame.size.width)
backgroundImageView.frame = frame
}
Anyone have any suggestions?
You have to add the following in your viewDidLoad function:
self.view.clipsToBounds = true or scrollView.clipsToBounds = true if you just want to clip the subviews in your UIScrollView.
Setting this value to true causes subviews to be clipped to the bounds of the receiver. If set to false, subviews whose frames extend beyond the visible bounds of the receiver are not clipped. The default value is false.
From Apple' doc : https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIView_Class/index.html#//apple_ref/occ/instp/UIView/clipsToBounds

How to create percentage of total width using autolayout?

I need to create three dynamic columns, each with a fixed percentage of the total width. Not thirds, but different values. For example, the following illustration shows three columns: the first being 42% wide, the second being 25% wide, and the third being 33% wide.
For a 600 pixel across viewcontroller, that would be 252, 150, and 198 pixels respectively.
However, for any subsequent display sizes (i.e. iPhone 4 landscape (960 wide) or iPad 2 portrait (768 wide), I would like the relative percentages to be the same (not the pixel widths quoted above).
Is there a way to do this using Storyboards (i.e. without code)? I can do this easily in code, but my goal is to put as much of this display logic as possible into the Storyboard.
If, as you say, you know how to do it in code, then you already know how to do it in the storyboard. It's exactly the same constraints, but you are creating them visually rather than in code.
Select both a view and its superview.
Choose Editor -> Pin -> Widths Equally to constrain the width to be equal to the superview's width (actually the "pin" popup dialog at the bottom of the canvas works best here).
Edit the constraint and set the Multiplier to the desired fraction, e.g. 0.42. And so too for the other views.
As Apple introduces UIStackView it made job much easy.
Method 1: Using Nib/StoryBoard:
You have to just add three view in interface builder & embed them into stackview
Xcode ► Editor ► Embed in ► StackView
Select stackView & give constraint with leading, trailing, top & equal height with safeArea
Click to Attribute inspector area &
Set StackView horizontal & distribution to fill proportionally
[3
Give constraint of three view with leading, trailing, top, bottom with respective of sides.
Method 2: Programmatically:
import UIKit
class StackViewProgramatically: UIViewController {
var propotionalStackView: UIStackView!
///Initially defining three views
let redView: UIView = {
let view = UIView()//taking 42 % initially
view.frame = CGRect(x: 0, y: 0, width: 42 * UIScreen.main.bounds.width/100, height: UIScreen.main.bounds.height)
view.backgroundColor = .red
return view
}()
let greenView: UIView = {
let view = UIView()//taking 42* initially
view.frame = CGRect(x: 42 * UIScreen.main.bounds.width/100, y: 0, width: 25 * UIScreen.main.bounds.width/100, height: UIScreen.main.bounds.height)
view.backgroundColor = .green
return view
}()
let blueView: UIView = {
let view = UIView()//taking 33*initially
view.frame = CGRect(x: 67 * UIScreen.main.bounds.width/100, y: 0, width: 33 * UIScreen.main.bounds.width/100, height: UIScreen.main.bounds.height)
view.backgroundColor = .blue
return view
}()
///Changing UIView frame to supports landscape mode.
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
DispatchQueue.main.async {
self.redView.frame = CGRect(x: 0, y: 0, width: 42 * self.widthPercent, height: self.screenHeight)
self.greenView.frame = CGRect(x: 42 * self.widthPercent, y: 0, width: 25 * self.widthPercent, height: self.screenHeight)
self.blueView.frame = CGRect(x: 67 * self.widthPercent, y: 0, width: 33 * self.widthPercent, height: self.screenHeight)
}
}
override func viewDidLoad() {
super.viewDidLoad()
//Adding subViews to the stackView
propotionalStackView = UIStackView()
propotionalStackView.addSubview(redView)
propotionalStackView.addSubview(greenView)
propotionalStackView.addSubview(blueView)
propotionalStackView.spacing = 0
///setting up stackView
propotionalStackView.axis = .horizontal
propotionalStackView.distribution = .fillProportionally
propotionalStackView.alignment = .fill
view.addSubview(propotionalStackView)
}
}
//MARK: UIscreen helper extension
extension NSObject {
var widthPercent: CGFloat {
return UIScreen.main.bounds.width/100
}
var screenHeight: CGFloat {
return UIScreen.main.bounds.height
}
}
Output:
Works with landscape & portrait
Demo project - https://github.com/janeshsutharios/UIStackView-with-constraints
https://developer.apple.com/videos/play/wwdc2015/218/
I think this can be explained in more detail so it can be more easily applied to any number of views requiring fixed percentage layouts within a superview.
Left-most view
Anchored to SuperView.Leading
Defines its fixed percentage as a multiplier on the SuperView.Height
Intermediate views
Defines its fixed percentage as a multiplier on the SuperView.Height
Pins its left to its neighbor's right
Right-Most view
Does not define a fixed percentage (it is the remainder of the available view)
Pins its left to its neighbor's right
Pins its right to SuperView.Trailing
All Views
Define their non-fixed heights by anchoring to Top Layout Guide.Top and Top Layout Guide.bottom. In the answer above, it is noted that this can also be done by setting equal height to the neighboring view.

Resources