Making UIScrollView with an UIImageView scale content automatically using Swift, autolayout and SnapKit - swift

I have a view controller set up in Interface Builder with the following view structure:
- UIView
* UIScrollView
* UIImageView
I want to assign an image to UIImageView so, that the scroll view (and it's content size) will adopt the same width than in the image and automatically calculate the height of the content size according to the image aspect ratio. (This makes the image vertically scrollable inside the UIScrollView.)
I'm using Swift and Interface Builder + SnapKit for managing autolayout constraints.

I managed to make the constraints using SnapKit in the following way:
In Interface Builder, select the view controller and create all missing constraints by selecting Editor / Resolve Auto Layout Issues / Add Missing Constraints
Manually select all generated constraints and select Placeholder / Remove at build time from Attributes Inspector
Assign UIScrollView and UIImageView outlets to the view controller
Assign an image to the UIImageView
Then implement the view controller as follows:
import UIKit
import SnapKit
class ViewController: UIViewController {
#IBOutlet weak var scrollView: UIScrollView!
#IBOutlet weak var imageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
scrollView.snp.makeConstraints { (make) in
make.edges.equalTo(view)
}
let ratio = imageView.image!.size.height / imageView.image!.size.width
imageView.snp.makeConstraints { (make) in
make.top.equalTo(0.0)
make.bottom.equalTo(scrollView.snp.bottom)
make.width.equalTo(view.snp.width)
make.height.equalTo(scrollView.snp.width).multipliedBy(ratio)
}
}
}

Related

No scrollbars appear when adding a custom view via addSubview to NSScrollView

I dynamically want to add a custom NSView as a content of a NSScrollView via code in runtime.
So I added a NSScrollView to my NSWindow, created an outlet to this NSScrollView and add my custom view.
#IBOutlet var myScrollView: NSView!
#IBOutlet var myCustomView: NSView!
myScrollView.addSubview(myCustomView)
This works fine (the content of myCustomView is shown in the NSScrollView) but the NSScrollView scrollbars are disabled and the content (that is larger than the NSScrollView) cannot be scrolled.
Is there something I am missing?
Thanks!
To add scrollable content to a NSScrollView you should set documentView and not via addSubview(_:)
let myView = MyView(frame: <somerect>)
scrollView.documentView = myView
If scrolling is still not working you may need to specify contentSize
Docs for NSScrollView

Flipping the positions of items in a UIStackView

I have a UIStackView, created in Interface Builder that contains two views. Now I'm trying to programatically change the order of these views (exchange their positions, so to speak).
Is there an easy fix for this that DOES NOT use Interface Builder to accomplish this?
Thanks :).
Create a layout with 2 UIViews in a UIStackView like so:
Create an outlet for your UIStackView to your UIViewController. Call addArrangedSubview on your UIStackView in viewDidLoad to change the order. See snippet below.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var stackView: UIStackView!
override func viewDidLoad() {
super.viewDidLoad()
// Switches order of the stackView
stackView.addArrangedSubview(self.stackView.subviews[0])
}
}
This should be the result:
The addArrangedSubview takes the given view and adds it to the end of the arrangedSubviews array. In our case we take the red view at index[0] and add it on the end (right) of the UIStackView.
You can change the view semantic of UIStackView from Unspecified to Force Right-to-Left to swap the position of of nested view.
you can use addArragnedSubview to reorder the views.
Only addArragnedSubview is needed because when adding the view to a new parent it is removed from the old parent.
if self.viewsSwitched{
stackview.addArrangedSubview(self.stackview.subviews[0])
self.viewsSwitched = false
}else{
stackview.addArrangedSubview(self.stackview.subviews[1])
self.viewsSwitched = true
}
This code exchanges the two views inside the stackview by each call

Custom views in Horizontal scroll view. Is it possible?

I am designing an app which has a screen in which I have a horizontal scroll view which I fill with UIViews dynamically depending upon the number of data I have in my array . I did the same via programmatically. I have mentioned my approach below.
1) I put a Scroll view for scrolling horizontally and created a reference for that in my class.
2) I programatically added views as per my code -
var imagevieww = UIImageView()
#IBOutlet weak var hrzntlscrl: UIView!
#IBOutlet weak var scrollview: UIScrollView!
override func viewDidLoad()
{
super.viewDidLoad()
let viewcount = 15
for var i = 0; i < viewcount; i++
{
let viewnew = UIView(frame: CGRectMake( hrzntlscrl.frame.origin.x+110*CGFloat(i), 0, 100.0, hrzntlscrl.frame.height))
viewnew.backgroundColor = UIColor.orangeColor()
imagevieww = UIImageView(frame: CGRectMake(0, 10, 100.0, 50))
imagevieww.backgroundColor = UIColor.blackColor()
viewnew.addSubview(imagevieww)
scrollview.addSubview(viewnew)
}
}
So I just wanted to know that instead of creating a view and the corresponding subviews eg. here imageview and setting their location and frame size programatically , Can I have a standard custom view designed in my IB and use any reference of that in my for loop instead of creating one programmatically? If we can do that,can you please give me some steps.
Yes. This is possible. You can instantiate a class from a nib with
let customView: CustomView = NSBundle.mainBundle().loadNibNamed("CustomViewNibName", owner: self, options: nil)[safe: 0] as? CustomView
You would also need to set the content size of the horizontal scroll view to the combined width of all the views.
But I think that your use case would be better served by using a UICollectionView instad of a scroll view, UICollectionView does all this and more in a much simpler implementation.

Set of UIImageView.Image re-positions parent view

I have a UIView and added a UIImageView as child to it. I later change the position of the UIView via another UIButton event.
If I change the image of the UIImageView the UIView - inc. all shields - moves back to the initial position.
Could anyone maybe explain me what is going on here :-D How do I keep the new position of the UIView ?
Here a image screen I took to visualize the issue
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var number: UIImageView!
#IBOutlet weak var popup: UIView!
#IBAction func openPopup(sender: UIButton) {
self.popup.center = sender.center
//old position: (190.5, 124.0) - new Position: (173.0, 414.0)
}
#IBAction func updateImage(sender: UIButton) {
self.number.image = UIImage(named: "img-1")
}
}
Update:
I found out that above code works if I deactivate autolayout. Here is a solution to do this with auto layout:
Auto Layout center UIView above another UIView at its center
Are you sure of that? Did you check by logging popup.frame.origin after updateImage being called? Have you properly set clipsToBounds and contentMode properties for number uiimageview?
I found out that above code works if I deactivate "Use Auto Layout" in Interface Builder Document section at tab File Inspector.
In addition here is a working solution to the issue using auto layout and constraints:
Auto Layout center UIView above another UIView at its center

How to apply Swift constraints on imageView on rotation in code?

The following elegant code derived from the new text by Vandad Nahavandipoor on Swift works fine in landscape however since the scrollView is constructed by code one cannot use usual constraints to properly display the chart named CrossTalk.png in landscape. In fact in landscape the chart initially displays in 50% of the screen and when dragged to the right in an attempt to fill the screen the initiating viewController-1 that segued into the current, displaying viewController-2 takes over the entire landscape view. Is there a way to fix this problem in code?
SWIFT latest version 6.1
import UIKit
class ChartViewController: UIViewController, UIScrollViewDelegate,UIGestureRecognizerDelegate {
var imageView: UIImageView?
var scrollView: UIScrollView?
let image = UIImage(named: "CrossTalk.png")
override func viewDidLoad() {
super.viewDidLoad()
imageView = UIImageView(image: image)
scrollView = UIScrollView(frame: view.bounds)
if let theScrollView = scrollView{
theScrollView.addSubview(imageView!)
theScrollView.contentSize = imageView!.bounds.size
view.addSubview(theScrollView)
}
}