Scroll view parallax effect - swift

I made a program from one youtube channel and ran into a problem. I think that it is related to the layout. On different devices displayed differently. And can someone tell me how to fix what text will fit onto another one and how to make the image appear on the whole my CustomView.
import UIKit struct scrollViewDataStruct {
let title: String?
let image: UIImage?
} class ScrollController: UIViewController, UIScrollViewDelegate{
#IBOutlet weak var scrollView: UIScrollView!
var scrollViewData = [scrollViewDataStruct]()
var viewTagValue = 10
var tagValue = 100
override func viewDidLoad() {
super.viewDidLoad()
scrollView.delegate = self
scrollViewData = [scrollViewDataStruct.init(title: "There was written a very large line that climbs to another line", image: #imageLiteral(resourceName: "knowledge_graph_logo")),
scrollViewDataStruct.init(title: "Second", image: #imageLiteral(resourceName: "knowledge_graph_logo"))]
scrollView.contentSize.width = self.scrollView.frame.width * CGFloat(scrollViewData.count)
var i = 0
for data in scrollViewData {
let view = CustomView(frame: CGRect(x: 10 + (self.scrollView.frame.width * CGFloat(i)), y: 200, width: self.scrollView.frame.width - 75, height: self.scrollView.frame.height - 90))
view.imageView.image = data.image
view.tag = i + viewTagValue
self.scrollView.addSubview(view)
let label = UILabel(frame: CGRect.init(origin: CGPoint.init(x: 0, y: 20), size: CGSize.init(width: 0, height: 40)))
label.text = data.title
label.font = UIFont.boldSystemFont(ofSize: 30)
label.textColor = UIColor.black
label.sizeToFit()
label.tag = i + tagValue
if i == 0 {
label.center.x = view.center.x
} else {
label.center.x = view.center.x - self.scrollView.frame.width / 2
}
self.scrollView.addSubview(label)
i += 1
}
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if scrollView == scrollView {
for i in 0..<scrollViewData.count {
let label = scrollView.viewWithTag(i + tagValue) as! UILabel
let view = scrollView.viewWithTag(i + viewTagValue) as! CustomView
var scrollContentOffset = scrollView.contentOffset.x + self.scrollView.frame.width
var viewOffset = (view.center.x - scrollView.bounds.width / 4) - scrollContentOffset
label.center.x = scrollContentOffset - ((scrollView.bounds.width / 4 - viewOffset) / 2)
}
}
}}class CustomView: UIView {
let imageView: UIImageView = {
let imageView = UIImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.backgroundColor = UIColor.darkGray
imageView.contentMode = .scaleAspectFit
return imageView
}()
override init(frame: CGRect) {
super.init(frame: frame)
self.addSubview(imageView)
imageView.leftAnchor.constraint(equalTo: self.leftAnchor).isActive = true
imageView.rightAnchor.constraint(equalTo: self.rightAnchor).isActive = true
imageView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
imageView.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}}
This is a launch on iPhone 5s
This is a launch on iPhone 8 plus

What you should be doing is setting the width of each UILabel to the size of the CustomView that contains the image, and setting each label's "numberOfLines" to 0 and set the lineBreak to wordWrap. This should, in theory, let the labels only be the width of the images AND let the UILabels fit the size of the text vertically, rather than horizontally - which is what sizeToFit does.

Related

How to display image view amongst stack view

I am trying to display an image view to the right side of Root stack view. I have set the size and width but for some reason it’s not showing my guess is because the rootstock is in a vertical position. If I put the rootstock view in a horizontal position, the whole thing scatters. How can I fix this? Image below show how it looks like
This how it look like if stack view is in horizontal position.
class UserCell: UIView {
var rootStack = UIStackView()
var userInfoStackView = UIStackView()
var subtitleStackView = UIStackView()
var dateInfoStackView = UIStackView()
var nameLabel = UILabel()
var compCode = UILabel()
var captionLabel = UILabel()
var dateLabel2 = UILabel()
var imageView = UIImageView()
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .white
setUPViews()
addComponents()
layoutComponents()
}
required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") }
private func setUPViews(){
rootStack.translatesAutoresizingMaskIntoConstraints = false
rootStack.alignment = .center
rootStack.axis = .vertical
rootStack.alignment = .leading
rootStack.spacing = 3
subtitleStackView.translatesAutoresizingMaskIntoConstraints = false
subtitleStackView.spacing = 2
dateInfoStackView.translatesAutoresizingMaskIntoConstraints = false
dateInfoStackView.spacing = 2
nameLabel.font = UIFont.systemFont(ofSize: 20, weight: .bold)
nameLabel.text = "Rolls Royce"
compCode.font = UIFont.systemFont(ofSize: 20, weight: .semibold)
compCode.text = "(RTYD8NTV001)"
captionLabel.font = UIFont.systemFont(ofSize: 9, weight: .semibold)
captionLabel.textColor = .darkGray
captionLabel.text = "Best Customer Since 07/01/2019"
dateLabel2.font = UIFont.systemFont(ofSize: 13, weight: .bold)
dateLabel2.text = "July 2022"
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.image = UIImage(systemName: "print")
imageView.layer.cornerRadius = 20
imageView.clipsToBounds = true
}
private func addComponents() {
rootStack.addArrangedSubview(imageView)
userInfoStackView.addArrangedSubview(nameLabel)
userInfoStackView.addArrangedSubview(compCode)
subtitleStackView.addArrangedSubview(captionLabel)
dateInfoStackView.addArrangedSubview(dateLabel2)
dateInfoStackView.addArrangedSubview(imageView)
addSubview(rootStack)
}
private func layoutComponents() {
rootStack.addArrangedSubview(userInfoStackView)
rootStack.addArrangedSubview(subtitleStackView)
rootStack.addArrangedSubview(dateInfoStackView)
rootStack.snp.makeConstraints { (make) in
make.edges.equalToSuperview()
}
imageView.snp.makeConstraints { (make) in
make.width.height.equalTo(40)
}
}
}
let userCell = UserCell(frame: CGRect(x: 0, y: 0, width: 500, height: 60))
PlaygroundPage.current.liveView = userCell

How can I position these UIView elements from the right using CGRect to position

I have a UIView sub class that allows me to create a group of 'tags' for the footer of some content. At the moment however they are position aligned to the left edge, I would like them to be positioned from the right.
I have included a playground below that should run the screen shot you can see.
The position is set within the layoutSubviews method of CloudTagView.
I tried to play around with their position but have not been able to start them from the right however.
import UIKit
import PlaygroundSupport
// CLOUD VIEW WRAPPER - THIS IS THE CONTAINER FOR THE TAGS AND SETS UP THEIR FRAME
class CloudTagView: UIView {
weak var delegate: TagViewDelegate?
override var intrinsicContentSize: CGSize {
return frame.size
}
var removeOnDismiss = true
var resizeToFit = true
var tags = [TagView]() {
didSet {
layoutSubviews()
}
}
var padding = 5 {
didSet {
layoutSubviews()
}
}
var maxLengthPerTag = 0 {
didSet {
layoutSubviews()
}
}
public override init(frame: CGRect) {
super.init(frame: frame)
isUserInteractionEnabled = true
clipsToBounds = true
}
public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
isUserInteractionEnabled = true
clipsToBounds = true
}
override func layoutSubviews() {
for tag in subviews {
tag.removeFromSuperview()
}
var xAxis = padding
var yAxis = padding
var maxHeight = 0
for (index, tag) in tags.enumerated() {
setMaxLengthIfNeededIn(tag)
tag.delegate = self
if index == 0 {
maxHeight = Int(tag.frame.height)
}else{
let expectedWidth = xAxis + Int(tag.frame.width) + padding
if expectedWidth > Int(frame.width) {
yAxis += maxHeight + padding
xAxis = padding
maxHeight = Int(tag.frame.height)
}
if Int(tag.frame.height) > maxHeight {
maxHeight = Int(tag.frame.height)
}
}
tag.frame = CGRect(x: xAxis, y: yAxis, width: Int(tag.frame.size.width), height: Int(tag.frame.size.height))
addSubview(tag)
tag.layoutIfNeeded()
xAxis += Int(tag.frame.width) + padding
}
if resizeToFit {
frame = CGRect(x: frame.origin.x, y: frame.origin.y, width: frame.size.width, height: CGFloat(yAxis + maxHeight + padding))
}
}
// MARK: Methods
fileprivate func setMaxLengthIfNeededIn(_ tag: TagView) {
if maxLengthPerTag > 0 && tag.maxLength != maxLengthPerTag {
tag.maxLength = maxLengthPerTag
}
}
}
// EVERYTHING BELOW HERE IS JUST SETUP / REQUIRED TO RUN IN PLAYGROUND
class ViewController:UIViewController{
let cloudView: CloudTagView = {
let view = CloudTagView(frame: .zero)
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
let tags = ["these", "are", "my", "tags"]
tags.forEach { tag in
let t = TagView(text: tag)
t.backgroundColor = .darkGray
t.tintColor = .white
cloudView.tags.append(t)
}
view.backgroundColor = .white
view.addSubview(cloudView)
NSLayoutConstraint.activate([
cloudView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
cloudView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
cloudView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
cloudView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor)
])
}
}
// Tag View
class TagView: UIView {
weak var delegate: TagViewDelegate?
var text = "" {
didSet {
layoutSubviews()
}
}
var marginTop = 5 {
didSet {
layoutSubviews()
}
}
var marginLeft = 10 {
didSet {
layoutSubviews()
}
}
var iconImage = UIImage(named: "close_tag_2", in: Bundle(for: CloudTagView.self), compatibleWith: nil) {
didSet {
layoutSubviews()
}
}
var maxLength = 0 {
didSet {
layoutSubviews()
}
}
override var backgroundColor: UIColor? {
didSet {
layoutSubviews()
}
}
override var tintColor: UIColor? {
didSet {
layoutSubviews()
}
}
var font: UIFont = UIFont.systemFont(ofSize: 12) {
didSet {
layoutSubviews()
}
}
fileprivate let dismissView: UIView
fileprivate let icon: UIImageView
fileprivate let textLabel: UILabel
public override init(frame: CGRect) {
dismissView = UIView()
icon = UIImageView()
textLabel = UILabel()
super.init(frame: frame)
isUserInteractionEnabled = true
addSubview(textLabel)
addSubview(icon)
addSubview(dismissView)
dismissView.isUserInteractionEnabled = true
textLabel.isUserInteractionEnabled = true
dismissView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(TagView.iconTapped)))
textLabel.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(TagView.labelTapped)))
backgroundColor = UIColor(white: 0.0, alpha: 0.6)
tintColor = UIColor.white
}
public required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
public init(text: String) {
dismissView = UIView()
icon = UIImageView()
textLabel = UILabel()
super.init(frame: CGRect(x: 0, y: 0, width: 0, height: 0))
isUserInteractionEnabled = true
addSubview(textLabel)
addSubview(icon)
addSubview(dismissView)
dismissView.isUserInteractionEnabled = true
textLabel.isUserInteractionEnabled = true
dismissView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(TagView.iconTapped)))
textLabel.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(TagView.labelTapped)))
self.text = text
backgroundColor = UIColor(white: 0.0, alpha: 0.6)
tintColor = UIColor.white
}
override func layoutSubviews() {
icon.frame = CGRect(x: marginLeft, y: marginTop + 4, width: 8, height: 8)
icon.image = iconImage?.withRenderingMode(.alwaysTemplate)
icon.tintColor = tintColor
let textLeft: Int
if icon.image != nil {
dismissView.isUserInteractionEnabled = true
textLeft = marginLeft + Int(icon.frame.width ) + marginLeft / 2
} else {
dismissView.isUserInteractionEnabled = false
textLeft = marginLeft
}
textLabel.frame = CGRect(x: textLeft, y: marginTop, width: 100, height: 20)
textLabel.backgroundColor = UIColor(white: 0, alpha: 0.0)
if maxLength > 0 && text.count > maxLength {
textLabel.text = text.prefix(maxLength)+"..."
}else{
textLabel.text = text
}
textLabel.textAlignment = .center
textLabel.font = font
textLabel.textColor = tintColor
textLabel.sizeToFit()
let tagHeight = Int(max(textLabel.frame.height,14)) + marginTop * 2
let tagWidth = textLeft + Int(max(textLabel.frame.width,14)) + marginLeft
let dismissLeft = Int(icon.frame.origin.x) + Int(icon.frame.width) + marginLeft / 2
dismissView.frame = CGRect(x: 0, y: 0, width: dismissLeft, height: tagHeight)
frame = CGRect(x: Int(frame.origin.x), y: Int(frame.origin.y), width: tagWidth, height: tagHeight)
layer.cornerRadius = bounds.height / 2
}
// MARK: Actions
#objc func iconTapped(){
delegate?.tagDismissed?(self)
}
#objc func labelTapped(){
delegate?.tagTouched?(self)
}
}
// MARK: TagViewDelegate
#objc protocol TagViewDelegate {
#objc optional func tagTouched(_ tag: TagView)
#objc optional func tagDismissed(_ tag: TagView)
}
extension CloudTagView: TagViewDelegate {
public func tagDismissed(_ tag: TagView) {
delegate?.tagDismissed?(tag)
if removeOnDismiss {
if let index = tags.firstIndex(of: tag) {
tags.remove(at: index)
}
}
}
public func tagTouched(_ tag: TagView) {
delegate?.tagTouched?(tag)
}
}
let viewController = ViewController()
PlaygroundPage.current.liveView = viewController
PlaygroundPage.current.needsIndefiniteExecution = true
UIStackView can line subviews up in a row for you, including with trailing alignment. Here is a playground example:
import SwiftUI
import PlaygroundSupport
class V: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let tags = ["test", "testing", "test more"].map { word -> UIView in
let label = UILabel()
label.text = word
label.translatesAutoresizingMaskIntoConstraints = false
let background = UIView()
background.backgroundColor = .cyan
background.layer.cornerRadius = 8
background.clipsToBounds = true
background.addSubview(label)
NSLayoutConstraint.activate([
background.centerXAnchor.constraint(equalTo: label.centerXAnchor),
background.centerYAnchor.constraint(equalTo: label.centerYAnchor),
background.widthAnchor.constraint(equalTo: label.widthAnchor, constant: 16),
background.heightAnchor.constraint(equalTo: label.heightAnchor, constant: 16),
])
return background
}
let stack = UIStackView.init(arrangedSubviews: [UIView()] + tags)
stack.translatesAutoresizingMaskIntoConstraints = false
stack.axis = .horizontal
stack.alignment = .trailing
stack.spacing = 12
view.addSubview(stack)
NSLayoutConstraint.activate([
stack.topAnchor.constraint(equalTo: view.topAnchor),
stack.widthAnchor.constraint(equalTo: view.widthAnchor),
])
view.backgroundColor = .white
}
}
PlaygroundPage.current.liveView = V()

Change label's font size dynamically when scrolling tableView

I have tableView and on top of it an imageView. Made stretchable header with this guide https://github.com/abhimuralidharan/StretchableTableViewHeader-Swift
And now I have to add label on my image. This label should change size/font when tableView is scrolling.
I created my imageView in another class:
class LotteryHeaderView: UIImageView {
let ticketsCountLabel: UILabel = {
let label = UILabel()
label.textColor = .white
label.translatesAutoresizingMaskIntoConstraints = false
label.font = UIFont.boldSystemFont(ofSize: 100)
return label
}()
let infoLabel: UILabel = {
let label = UILabel()
label.textColor = .white
label.textAlignment = .center
label.translatesAutoresizingMaskIntoConstraints = false
label.font = UIFont.systemFont(ofSize: 13, weight: .medium)
label.numberOfLines = 2
label.text = Localizable.all_user_tickets_count()
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
self.addSubViews([ticketsCountLabel, infoLabel])
ticketsCountLabel.centerX(to: self.centerXAnchor)
.centerY(to: self.centerYAnchor)
infoLabel.top(to: ticketsCountLabel.bottomAnchor, constant: 7)
.width(constant: 184)
.centerX(to: self.centerXAnchor)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Initialized it in my controller:
let imageView = LotteryHeaderView(frame: CGRect(x: 0, y: 0, width: Constants.Size.screenWidth, height: 300))
Setup in viewDidLoad:
imageView.frame = CGRect(x: 0, y: 0, width: Constants.Size.screenWidth, height: 300)
imageView.image = R.image.santa_fe()
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
view.addSubview(imageView)
Here func which gets called every time the tableview is scrolled, and it works for my image (it collapsable and stretchable):
func scrollImageInHeader(scrollView: UIScrollView) {
let y = 300 - (scrollView.contentOffset.y + 300)
let height = min(max(y, 60), 450)
imageView.frame = CGRect(x: 0, y: 0, width: Constants.Size.screenWidth, height: height)
}
Tried to add in scrollImageInHeader() code under, from this StackOverFlowAnswer, but it doesn't help
let offset = scrollView.contentOffset.y
let scale = min(max(1.0 - offset / 200.0, 0.0), 1.0)
imageView.ticketsCountLabel.transform = CGAffineTransform(scaleX: scale, y: scale)
Please, any help will be appreciated.
UIScrollView is parent UITableViewController. You use UIScrollView with UIScrollViewDelegate for setup label font size with scrollOffset: CGFloat follow .y
extension ViewController: UIScrollViewDelegate{
func scrollViewDidScroll(_ scrollView: UIScrollView) {
var scrollOffset : CGFloat = scrollView.contentOffset.y
// process with scrollOffset
}
}
Actually I found my mistake. Code from Here helped.
Class LotteryHeaderView I changed to UIView and added there an UIImageView.

UIScrollview doesn't work once subviews added

I'm new to working with ScrollViews, and I'm doing it all programatically. I must be missing something super simple, but when I have no subviews, the scrollview shows up properly and scrolls up and down. But any time I add any subviews, the whole thing refuses to show up at all.
class DetailedPostScrollView: UIScrollView {
let topLabel: UILabel = {
let label = UILabel()
label.text = "this is the top"
return label
}()
let bottomLabel: UILabel = {
let label = UILabel()
label.text = "this is the bottom"
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
contentSize = CGSize(width: frame.width, height: 2000)
alwaysBounceVertical = true
addSubviewUsingAutoLayout(topLabel, bottomLabel)
topLabel.centerXAnchor.constrain(to: self.centerXAnchor)
topLabel.widthAnchor.constrain(to: 200)
topLabel.heightAnchor.constrain(to: 50)
topLabel.topAnchor.constrain(to: self.topAnchor, with: 100)
bottomLabel.centerXAnchor.constrain(to: self.centerXAnchor)
bottomLabel.widthAnchor.constrain(to: 200)
bottomLabel.heightAnchor.constrain(to: 50)
bottomLabel.bottomAnchor.constrain(to: self.bottomAnchor)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
And in my viewController, I instantiate and add the scroll view
let detailedPostScrollView = DetailedPostScrollView(frame: UIScreen.main.bounds)
detailedPostScrollView.backgroundColor = UIColor.purple
view.addSubviewUsingAutoLayout(detailedPostScrollView)
Again, I"m sure it's something super simple but I checked out all the questions and tutorial videos and couldn't see where I"m going wrong. Thanks for your help.
Edit: It seems to work fine when I do it all programmatically from a viewcontroller, as follows :
let scrollView: UIScrollView = {
let sv = UIScrollView()
sv.contentSize = CGSize(width: view.frame.width, height: 2000)
sv.backgroundColor = UIColor.purple
return sv
}()
view.addSubviewUsingAutoLayout(scrollView)
scrollView.topAnchor.constrain(to: view.topAnchor, with: 100)
scrollView.leadingAnchor.constrain(to: view.leadingAnchor)
scrollView.trailingAnchor.constrain(to: view.trailingAnchor)
scrollView.bottomAnchor.constrain(to: view.bottomAnchor)
let topLabel: UILabel = {
let label = UILabel()
label.text = "this is the top"
return label
}()
scrollView.addSubviewUsingAutoLayout(topLabel)
topLabel.centerXAnchor.constrain(to: scrollView.centerXAnchor)
topLabel.widthAnchor.constrain(to: 200)
topLabel.heightAnchor.constrain(to: 50)
topLabel.topAnchor.constrain(to: scrollView.topAnchor)
Something is off when I create a custom scrollview and instantiate that in my vc.
Try this :
class ViewController : UIViewController , UIScrollViewDelegate{
var scrollView : UIScrollView! ;
var containerView = UIView();
var contentSize : CGSize {
get {
return CGSize(width: view.frame.width, height: 2000);
}
}
override func viewDidLoad() {
super.viewDidLoad();
setupScrollView();
setupItems();
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews();
self.scrollView.frame = self.view.bounds;
containerView.frame = CGRect(x: 0, y: 0, width: contentSize.width, height: contentSize.height);
}
private func setupScrollView() {
scrollView = UIScrollView();
scrollView.delegate = self;
scrollView.backgroundColor = .white;
scrollView.contentSize = contentSize;
self.view.addSubview(scrollView);
containerView.backgroundColor = .purple;
self.scrollView.addSubview(containerView);
}
private func setupItems() {
let topButton = UIButton();
topButton.translatesAutoresizingMaskIntoConstraints = false;
topButton.setTitle("TOP BUTTON", for: .normal);
topButton.setTitleColor(.white, for: .normal);
self.containerView.addSubview(topButton);
let bottomTextField = UITextField();
bottomTextField.translatesAutoresizingMaskIntoConstraints = false;
bottomTextField.borderStyle = .roundedRect;
bottomTextField.placeholder = "BOTTOM TEXT FIELD";
self.containerView.addSubview(bottomTextField);
topButton.centerXAnchor.constraint(equalTo: containerView.centerXAnchor).isActive = true;
topButton.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 50).isActive = true;
topButton.widthAnchor.constraint(equalToConstant: 100).isActive = true;
topButton.heightAnchor.constraint(equalToConstant: 50).isActive = true;
bottomTextField.centerXAnchor.constraint(equalTo: containerView.centerXAnchor).isActive = true;
bottomTextField.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: -50).isActive = true;
bottomTextField.widthAnchor.constraint(equalToConstant: 200).isActive = true;
bottomTextField.heightAnchor.constraint(equalToConstant: 40).isActive = true;
}
}
Set the scroll view frame to view bound in viewDidLayoutViews instead of using the layout , and add a container view as a subview for scroll view and set the size to same content size for scroll view again in viewDidLayoutViews so when ever user rotate the phone he get the correct width , and finally add all your views to container view .

UIScrollView showing subview not correctly

I'm trying to show simple custom view into scrollView. Here's my code :
struct scrollViewDataStruct {
let title: String?
let image: UIImage?
}
class ViewController: UIViewController {
#IBOutlet weak var scrollView: UIScrollView!
var scrollViewData = [scrollViewDataStruct]()
override func viewDidLoad() {
super.viewDidLoad()
scrollViewData = [
scrollViewDataStruct(title: "First", image: #imageLiteral(resourceName: "iPhone 8 Copy 2")),
scrollViewDataStruct(title: "Second", image: #imageLiteral(resourceName: "iPhone 8 Copy 3"))
]
self.scrollView.backgroundColor = .yellow
scrollView.contentSize.width = self.scrollView.frame.width * CGFloat(scrollViewData.count)
var i = 0
for _ in scrollViewData {
let view = CustomView(frame: CGRect(x: self.scrollView.frame.width * CGFloat(i), y: 0, width: scrollView.frame.width, height: self.scrollView.frame.height))
self.scrollView.addSubview(view)
i += 1
}
// Do any additional setup after loading the view, typically from a nib.
}
}
class CustomView: UIView {
let imageView: UIImageView = {
let imageView = UIImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.backgroundColor = UIColor.blue
return imageView
}()
override init(frame: CGRect) {
super.init(frame: frame)
self.addSubview(imageView)
imageView.leftAnchor.constraint(equalTo: self.leftAnchor).isActive = true
imageView.rightAnchor.constraint(equalTo: self.rightAnchor).isActive = true
imageView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
imageView.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
As you can see, the CustomView's frame = scrollView's frame but when i ran application it's not as I expected :
Then, in storyboard, i change device from iphone8 to iphone 8 plus and run again. It's show CustomView correctly. I have no idea, the scrollView is always correct but the CustomView is not .
Any suggest ?
Your problem is that you are accessing the frame of the scrollView before Auto Layout has run and established the size of the frame for the actual device. A quick fix is to move your setup code into an override of viewDidLayoutSubviews.
You have to be careful though, because unlike viewDidLoad, viewDidLayoutSubviews will run more than once, so you have to make sure you don't add your views multiple times.
// property - have we set up the views yet?
var setup = false
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if !setup {
scrollView.contentSize.width = self.scrollView.frame.width * CGFloat(scrollViewData.count)
var i = 0
for _ in scrollViewData {
let view = CustomView(frame: CGRect(x: self.scrollView.frame.width * CGFloat(i), y: 0, width: scrollView.frame.width, height: self.scrollView.frame.height))
self.scrollView.addSubview(view)
i += 1
}
setup = true
}
}
You should consider using constraints to place your views within your scrollView content instead of messing with the frame calculations, then Auto Layout would just automatically do the right thing.
In viewDidLoad, UI component will suppose to have the size you have taken in storyboard.
There are 2 ways to do this:
1. Use autoresizingMask property
autoresizingMask property will resize the view, if its containerView's frames changed
var i = 0
for _ in scrollViewData {
let view = CustomView(frame: CGRect(x: self.scrollView.frame.width * CGFloat(i), y: 0, width: scrollView.frame.width, height: self.scrollView.frame.height))
view.autoresizingMask = .flexibleHeight
self.scrollView.addSubview(view)
i += 1
}
2. Use fixed parameters, say UIScreen.main.bounds.size.height
Just update your code for custom view's height with reference to screen height rather than scroll view's height. It will work fine
var i = 0
let height = UIScreen.main.bounds.size.height
for _ in scrollViewData {
let view = CustomView(frame: CGRect(x: self.scrollView.frame.width * CGFloat(i), y: 0, width: scrollView.frame.width, height: height))
self.scrollView.addSubview(view)
i += 1
}