How to replicate UIKit/SwiftUI elements background (like the UITabBar/TabView background) - swift

I would like to replicate the background (including the same blur effect and the thin gray border and their adaptivity) of UITabBar (and similar UIKit/SwiftUI elements) and apply it to another UI element.
How can I accomplish that?
I've tried to copy it but when I stack my new view on other different color views the behavior is different (and neither the night mode is supported).
I don't care if the answer will be for UIKit or SwiftUI framework.

See UIColor extensions in UIInterface module, like below examples (there are many there):
* 1. systemBackground
* Use this stack for views with standard table views, and designs which have a white
* primary background in light mode.
*/
#available(iOS 13.0, *)
open class var systemBackground: UIColor { get }
/* 2. systemGroupedBackground
* Use this stack for views with grouped content, such as grouped tables and
* platter-based designs. These are like grouped table views, but you may use these
* colors in places where a table view wouldn't make sense.
*/
#available(iOS 13.0, *)
open class var systemGroupedBackground: UIColor { get }
/* Fill colors for UI elements.
* These are meant to be used over the background colors, since their alpha component is less than 1.
*
* systemFillColor is appropriate for filling thin and small shapes.
* Example: The track of a slider.
*/
#available(iOS 13.0, *)
open class var systemFill: UIColor { get }

Related

Is there a more elegant way for multiple attributes of a UIImage?

Is there a way to create like an array of attributes which I want a UIImage to have? Because I often have to do this repetitive pattern (I would like to get rid of the 'userImage.layer' part if possible):
override func awakeFromNib() {
super.awakeFromNib()
userImage.contentMode = .scaleAspectFill
userImage.layer.borderWidth = 1
userImage.layer.masksToBounds = false
userImage.layer.borderColor = UIColor.red.cgColor
userImage.layer.cornerRadius = self.frame.height / 2
self.clipsToBounds = true
// Initialization code
}
The short answer is: No officially supported way of grouping those attributes in UIKit*.
Many people end up building a thin "styling layer" on top of UIKit. Some approaches I've seen, from more popular to less popular:
Create some kind mapping of "styles" to attributes, and manually apply the style to the UIImage at some point in the view/view controller lifecycle (awakeFromNib is a good spot).
Subclass the class you want to style and create some kind of custom initializer that takes in a "style". You'd only end up with 1 subclass. Not every UIKit class can be safely subclassed, so YMMV.
Subclass the classes you want to style and have each subclass map to different styles. You'd end up with many subclasses. Not every UIKit class can be safely subclassed, so YMMV.
* There's UIAppearance, but not every attribute or class supports it. You can usually look at the header files to figure that out.

Using Swift, how do I animate the .setPosition() method of an NSSplitView without visually stretching its contents?

I would like to animate the appearance of a NSSplitViewItem using .setPosition() using Swift, Cocoa and storyboards. My app allows a student to enter a natural deduction proof. When it is not correct, an 'advice view' appears on the right. When it is correct, this advice view will disappear.
The code I'm using is the below, where the first function makes the 'advice' appear, and the second makes it disappear:
func showAdviceView() {
// Our window
let windowSize = view.window?.frame.size.width
// A CGFloat proportion currently held as a constant
let adviceViewProportion = BKPrefConstants.adviceWindowSize
// Position is window size minus the proportion, since
// origin is top left
let newPosition = windowSize! - (windowSize! * adviceViewProportion)
NSAnimationContext.runAnimationGroup { context in
context.allowsImplicitAnimation = true
context.duration = 0.75
splitView.animator().setPosition(newPosition, ofDividerAt: 1)
}
}
func hideAdviceView() {
let windowSize = view.window?.frame.size.width
let newPosition = windowSize!
NSAnimationContext.runAnimationGroup{ context in
context.allowsImplicitAnimation = true
context.duration = 0.75
splitView.animator().setPosition(newPosition, ofDividerAt: 1)
}
}
My problem is that the animation action itself is causing the text in the views to stretch, as you can see in this example: Current behaviour
What I really want is the text itself to maintain all proportions and slide gracefully in the same manner that we see when the user themselves moves the separator: Ideal behaviour (but to be achieved programmatically, not manually)
Thus far in my troubleshooting process, I've tried to animate this outside of NSAnimationContext; played with concurrent drawing and autoresizing of subviews in XCode; and looked generally into Cocoa's animation system (though much of what I've read doesn't seem to have direct application here, but I might well be misunderstanding it). I suspect what's going on is that the .animator() proxy object allows only alpha changes and stretches---redrawing so that text alignment is honoured during the animation might be too non-standard. My feeling is that I need to 'trick' the app into treating the animation as though it's being performed by the user, but I'm not sure how to go about that.
Any tips greatly appreciated...
Cheers

Display multiple overlaying widgets

I'm trying to display multiple cairo drawings overlapping each other:
extern crate cairo;
extern crate gio;
extern crate gtk;
use std::f64::consts::PI;
use gio::prelude::*;
use gtk::prelude::*;
use gtk::DrawingArea;
use std::env::args;
fn build_ui(application: &gtk::Application) {
let window = gtk::ApplicationWindow::new(application);
window.set_default_size(300, 300);
let overlay = gtk::Overlay::new();
// Draw first circle
let drawing_area1 = Box::new(DrawingArea::new)();
drawing_area1.connect_draw(|_, ctx| draw(ctx, 0.5, 0.4));
overlay.add(&drawing_area1);
// Draw second circle
let drawing_area2 = Box::new(DrawingArea::new)();
drawing_area2.connect_draw(|_, ctx| draw(ctx, 0.2, 1.0));
overlay.add(&drawing_area2);
window.add(&overlay);
window.show_all();
}
fn draw(ctx: &cairo::Context, width: f64, color: f64) -> Inhibit {
ctx.scale(300., 300.);
ctx.arc(0.5, 0.5, width, 0.0 * PI, 2.0 * PI);
ctx.set_source_rgba(color, 0.0, 0.0, 0.8);
ctx.fill_preserve();
Inhibit(false)
}
fn main() {
let application =
gtk::Application::new(Some("example.overlay"), Default::default())
.expect("Initialization failed...");
application.connect_activate(|app| {
build_ui(app);
});
application.run(&args().collect::<Vec<_>>());
}
Running this code gives me this warning:
(test_overlay_gtk:25534): Gtk-WARNING **: 19:12:05.573: Attempting to add a widget with type GtkDrawingArea to a GtkOverlay, but as a GtkBin subclass a GtkOverlay can only contain one widget at a time; it already contains a widget of type GtkDrawingArea
I understand that the overlay object can display only one of the drawing areas. I thought the overlay class is for exactly this purpose, to show overlapping widgets. I can't find a way to display the second overlapping drawing area.
add adds the widget to the overlay as the primary child - you can only have one of these. This is inherited from the container class in older versions of gtkmm (which I assume you're using) and is replaced by set_child in gtkmm 4 (which no longer inherits add from Gtk::Container).
add_overlay is the Gtk::Overlay specific method that allows you to add any number of widgets to be displayed on top of the child widget.
Try replacing your second add method with add_overlay and it should work.
First: I have no idea what I am doing here.
I simply asked Google for GtkOverlay and found this page: https://developer.gnome.org/gtk3/stable/GtkOverlay.html
The documentation for gtk_overlay_add_overlay says:
Adds widget to overlay.
The widget will be stacked on top of the main widget added with gtk_container_add().
So, apparently there is a main widget which you .add() and overlays which you .add_overlay().
For your example code: Can't you just draw the two drawings to the same overlay widget? Something like drawing_area1.connect_draw(|_, ctx| draw(ctx, 0.5, 0.4); draw(ctx, 0.2, 1.0));

iOS Autolayout - How to set two different distances between views, depends on the screen height

I know I'm missing something, because this has to be something easy to achieve.
My problem is that I have in my "loading screen" (the one that appears right after the splash) an UIImageView with two different images for 3.5" and 4" size screen. In a certain place of that images, I put one UIActivityIndicator, to tell the user that the app is loading something in the background. That place is not the same for both images, because one of them is obviously higher that the other, so I want to set an autolayout constraint that allows me to put that activity indicator at different heights, depends on if the app is running in an iPhone 5 or not.
Without Autolayout, I'd set the frame.origin.y of the view to 300 (for example), and then in the viewDidLoad method of the ViewController, I'd ask if the app is running in an iPhone 5, so I'd change the value to, for example, 350. I have no idea how to do this using Autolayout and I think it has to be pretty simple.
You can create an NSLayoutConstraint outlet on your view controller and connect the outlet to the activity indicator's Y constraint in your xib or storyboard. Then, add an updateViewContraints method to your view controller and update the constraint's constant according to the screen size.
Here's an example of updateViewConstraints:
- (void)updateViewConstraints {
[super updateViewConstraints];
self.activityIndicatorYConstraint.constant =
[UIScreen mainScreen].bounds.size.height > 480.0f ? 200 : 100;
}
Of course you will want to put in your appropriate values instead of 200 and 100. You might want to define some named constants. Also, don't forget to call [super updateViewConstraints].
The problem of #Rob answer's is you should do a lot of code for each constraint.
So to resolve that, just add ConstraintLayout class to your code and modify constraint constant value for the device that you want in the IB :
//
// LayoutConstraint.swift
// MyConstraintLayout
//
// Created by Hamza Ghazouani on 19/05/2016.
// Copyright © 2016 Hamza Ghazouani. All rights reserved.
//
import UIKit
#IBDesignable
class LayoutConstraint: NSLayoutConstraint {
#IBInspectable
var 📱3¨5_insh: CGFloat = 0 {
didSet {
if UIScreen.main.bounds.maxY == 480 {
constant = 📱3¨5_insh
}
}
}
#IBInspectable
var 📱4¨0_insh: CGFloat = 0 {
didSet {
if UIScreen.main.bounds.maxY == 568 {
constant = 📱4¨0_insh
}
}
}
#IBInspectable
var 📱4¨7_insh: CGFloat = 0 {
didSet {
if UIScreen.main.bounds.maxY == 667 {
constant = 📱4¨7_insh
}
}
}
#IBInspectable
var 📱5¨5_insh: CGFloat = 0 {
didSet {
if UIScreen.main.bounds.maxY == 736 {
constant = 📱5¨5_insh
}
}
}
}
Don't forgot to inherit your class constraint from ConstraintLayout
I will add the objective-c version soon
The basic tool in Auto Layout to manage UI objects' position is the Constraints. A constraint describes a geometric relationship between two views. For example, you might have a constraint that says:
“The right edge of progress bar is connected to the left edge of a lable 40 points of empty space between them.”
This means using AutoLayout you can't do conditional position setting based on UIDevice's mode, rather you can create a view layout which modifies itself if eg. the app runs on 3.5' full screen (IPhone4) or 4' full screen (IPhone5) based on the constraints.
So options for your problem using Constraints:
1) find a view on your layout which can be used to create a constraint to position the progressbar relatively. (select the view and the progressbar using CMD button, then use Editor/Pin/Vertical Spacing menu item to create a vertical constraint between the 2 objects)
2) create an absolute constraint to stick the progressbar's position to screen edge (keeping space) or centrally
I found helpful this tutorial about AutoLayout which might be beneficial for you also:
http://www.raywenderlich.com/20881/beginning-auto-layout-part-1-of-2
Pls note: autolayout only works from IOS 6.
The new way, Without writing a single line!
No need to write device based conditions like these :-
if device == iPhoneSE {
constant = 44
} else if device == iPhone6 {
constant = 52
}
I created a library Layout Helper so now you can update constraint for each device without writing a single line of code.
Step 1
Assign the NSLayoutHelper to your constraint
Step 2
Update the constraint for the device you want
Step 3
Run the app and see the MAGIC
I generally always try to stay in Interface Builder for setting up constraints. Diving in code to have more control is usually useful if you have completely different layouts on iPhone 4 and 6 for example.
As mentioned before, you can't have conditionals in Interface Builder, that's when linking a constraint to your view controller really comes handy.
Here's a short explanation on 3 approaches to solve Auto Layout issues for different screen sizes: http://candycode.io/how-to-set-up-different-auto-layout-constraints-for-different-screen-sizes/

Can I use icons with RGBA transparency when using GTK+ drag and drop?

I am in the process of adding drag and drop support to an existing Mono/C#/GTK# application. I was wondering whether it was possible to use RGBA transparency on the icons that appear under the mouse pointer when I start dragging an object.
So far, I realized the following:
I can set the bitmap in question by calling the Gtk.Drag.SourceSetIconPixbuf() method. However, no luck with alpha transparency: pixels that are not fully opaque would get 100% transparent this way.
I also tried calling RenderPixmapAndMask() on the GdkPixbuf so that I could use Gtk.Drag.SourceSetIcon() with an RGBA colormap of my Screen. It didn't work either: whenever I started dragging, I got the following error:
[Gdk] IA__gdk_window_set_back_pixmap: assertion 'pixmap == NULL || gdk_drawable_get_depth (window) == gdk_drawable_get_depth (pixmap)' failed.
This way, the pixmap doesn't even get copied, only a white shape (presumably set by the mask argument of SetSourceIcon()) shows up on dragging.
I'd like to ask if there's a way to make these icons have alpha transparency, despite the fact that I failed to do so. In case it's impossible, answers discussing the reasons of the lack of this feature would also be helpful. Thank you.
(Compositing is - of course - enabled on my desktop (Ubuntu/10.10, Compiz/0.8.6-0ubuntu9).)
Ok, finally I solved it. You should create a new Gtk.Window of POPUP type, set its Colormap to your screen's RGBA colormap, have the background erased by Cairo to a transparent color, draw whatever you'd like on it and finally pass it on to Gtk.Drag.SetIconWidget().
Sample code (presumably you'll want to use this inside OnDragBegin, or at a point where you have a valid drag context to be passed to SetIconWidget()):
Gtk.Window window = new Gtk.Window (Gtk.WindowType.Popup);
window.Colormap = window.Screen.RgbaColormap;
window.AppPaintable = true;
window.Decorated = false;
window.Resize (/* specify width, height */);
/* The cairo context can only be created when the window is being drawn by the
* window manager, so wrap drawing code into an ExposeEvent delegate. */
window.ExposeEvent += delegate {
Context ctx = Gdk.CairoHelper.Create (window.GdkWindow);
/* Erase the background */
ctx.SetSourceRGBA (0, 0, 0, 0);
ctx.Operator = Operator.Source;
ctx.Paint ();
/* Draw whatever you'd like to here, and then clean up by calling
Dispose() on the context's target. */
(ctx.Target as IDisposable).Dispose ();
};
Gtk.Drag.SetIconWidget(drag_context, window, 10, 10);